← Artículos

Un diálogo de permisos me borró la corrida de las 4am

· 6 min de lectura

Una automatización desatendida en macOS se cuelga para siempre si toca una carpeta protegida por TCC y el sistema decide preguntar en vez de denegar: la llamada se queda esperando una respuesta que nadie va a dar. El arreglo no es pedir más permisos, es acceder a esos archivos desde un binario que ya tenga el permiso concedido, y ponerle tiempo límite a toda llamada que cruce el disco.

Mi fábrica de contenido arranca sola a las 4:00 de la mañana. El 6 de agosto de 2026 amaneció sin entregar nada, y el log no tenía ni un error. Esto es lo que pasó, porque es un modo de fallo que le puede pegar a cualquier automatización que corra sin nadie mirando.

Qué pasó exactamente

A las 04:01:49 el sistema anotó esto: tccd Prompting for access to indirect object Dropbox launchd UserNotificationCenter spawned Traducido: macOS abrió una ventana preguntando si mi agente podía leer una carpeta de Dropbox. En una sesión normal yo pulso "Permitir" y sigue. A las 4 de la mañana no hay nadie. La llamada al sistema no falló. Se quedó esperando. El proceso dejó de emitir salida, el vigilante lo mató 45 minutos después, y el lanzador reintentó. Los tres reintentos siguientes se encolaron detrás de la misma ventana pendiente: por eso el log tiene una sola línea de aviso y cuatro corridas muertas. Total: de 04:00 a 07:36. Tres horas y treinta y seis minutos por una ventana que nadie vio.

Este sistema es el que enseño dentro de la comunidad MÁQUINA IA: las estaciones, los gates y el código que lo sostiene.

Por qué no lo detecta ningún gate

Todo mi pipeline está lleno de verificaciones: que el video dure lo que debe, que el paquete esté completo, que el contraste pase. Ninguna sirvió, porque un gate se ejecuta después de un paso. Este paso nunca terminó. Un error hace ruido. Un bloqueo es silencio, y el silencio se parece mucho a "todavía está trabajando". Ese fue el problema real: nada en el sistema distinguía "sigue procesando" de "está esperando a un humano que no existe".

El detalle que lo explica todo

Probé qué binarios podían leer esa carpeta desde un servicio de arranque real. Los resultados fueron distintos entre sí, y ahí está la lección:

  • El intérprete de Homebrew: funciona, porque ya tiene su permiso concedido.
  • El intérprete de sistema: falla con un error de permisos, limpio e inmediato.
  • El comando de listar archivos: falla en silencio, con código 1.
  • Mi agente: se cuelga, porque no es un binario de plataforma, así que macOS pregunta en vez de denegar.

Fallar rápido es un regalo. Lo que mata una automatización desatendida no es el "no", es el "a ver, déjame preguntarle a alguien". Y un detalle que cuesta caro aprender: no existe bandera que apague esto. Los permisos de este tipo los aplica el núcleo del sistema, no la aplicación. Puedes darle a tu herramienta todos los permisos internos que quieras; la ventana la abre el sistema operativo igual.

El arreglo, en tres decisiones

Uno: acceso indirecto. Ningún paso de la rutina toca esa carpeta directamente. Todo pasa por un pequeño script que corre con el intérprete que sí tiene el permiso, y que expone tres operaciones: comprobar, listar, ejecutar. El agente nunca pide el archivo; le pide el resultado a algo que ya puede leerlo. Dos: tiempo límite en todo lo que cruce el disco. Una llamada que puede quedarse esperando para siempre necesita un reloj encima. Si no responde en el plazo, se cancela y la rutina degrada con causa escrita, en vez de quedarse muda. Tres: el silencio también es un fallo. El vigilante ahora vigila la salida, no solo el reloj total. Un proceso que no emite nada durante minutos está tan roto como uno que devuelve un error, y hay que tratarlo igual.

Lo que me llevo

Llevaba semanas endureciendo el pipeline contra los fallos que sabía nombrar: créditos agotados, renders caídos, red intermitente. El que me tumbó el día fue una ventana de diálogo. Si automatizas algo que corre mientras duermes, hazte esta pregunta antes que ninguna otra: ¿qué pasa si el sistema decide preguntarle algo a un humano? Si la respuesta es "se espera", ya tienes tu próximo incidente agendado. El costo de este fue una madrugada. Escribirlo cuesta veinte minutos y sirve para siempre.

Preguntas frecuentes

¿Por qué se cuelga un script en vez de fallar?
Porque el sistema de permisos de macOS puede responder de tres formas: conceder, denegar o preguntar. Si el binario no está en la lista de binarios de plataforma, el sistema decide preguntar y abre una ventana. La llamada queda bloqueada esperando esa respuesta, y si no hay nadie frente al computador, espera de forma indefinida sin devolver ningún error.
¿Sirve darle permisos totales a la herramienta?
No, y es la confusión más cara. Los permisos internos de una herramienta de automatización solo controlan lo que esa herramienta se permite a sí misma. El control de acceso a carpetas protegidas lo aplica el núcleo del sistema operativo por debajo, así que la ventana aparece igual aunque tu configuración diga que todo está autorizado.
¿Cómo detecto que una automatización está bloqueada y no trabajando?
Vigilando la salida, no solo el tiempo total. Guarda la marca de tiempo de la última línea que escribió el proceso y compárala con el reloj: si lleva varios minutos sin emitir nada, trátalo como un fallo y córtalo. Un tiempo límite global no basta, porque llega demasiado tarde y no te dice en qué paso se quedó.
¿Cuál es el arreglo más rápido si me pasa hoy?
Mueve el acceso a la carpeta protegida fuera del agente: haz que un intérprete que ya tenga el permiso concedido haga la lectura y devuelva solo el resultado. Después ponle tiempo límite a esa llamada. Con esas dos piezas, el peor caso deja de ser una madrugada perdida y pasa a ser un paso que degrada con causa escrita.

Fuentes