Un cron job atascado ya no significa esperar al timeout: hermes cron cancel corta la ejecución en curso

Son las tres de la mañana y el móvil vibra: un colector que corre cada hora se ha quedado atascado contra una API externa que dejó de responder. Sigue ejecutándose, sigue esperando un timeout, sigue escribiendo la misma línea de error en el log. Abres una terminal, lanzas hermes cron pause y descubres que no ha detenido nada. Solo significa que la próxima hora no habrá disparo. La ejecución actual sigue ahí, quemando tokens línea a línea.
Ahí está el malentendido en el que todo el mundo tropieza: pause gobierna la programación, no la ejecución que ya está en marcha. Antes solo tenías dos salidas: esperar al timeout o borrar el job y volver a crearlo. La v0.22.6 añade la tercera: hermes cron cancel.
Qué hace realmente cancel
hermes cron cancel <job_id_or_name>
hermes cron cancel <job_id_or_name> --reason "wedged on the staging API"
La referencia del job acepta un ID hexadecimal o un nombre (sin distinguir mayúsculas). Los nombres ambiguos se rechazan listando los IDs candidatos, así que no puedes detener el job equivocado por accidente.
La programación no se toca: el job sigue habilitado y su próximo disparo ocurre según lo previsto. Lo único que cambia es la ejecución actual.
Cómo se detiene depende del tipo de job:
- Jobs de script (
--script, incluido el modo sin agente): se termina todo el árbol de procesos —SIGTERMal grupo de procesos del script, con escalada aSIGKILLtras un periodo de gracia, nietos en segundo plano incluidos. Es el mismo desmontaje que hace un timeout de script, así que no queda nada corriendo por detrás del job. - Jobs de agente: el turno en curso se interrumpe en su siguiente checkpoint.
Lo importante: esto es una petición, no un kill. La cancelación se registra contra el execution ID de esa ejecución y se recoge en su siguiente comprobación de cancelación (bastante menos de un segundo en un script). La ejecución anota entonces una fila cancelled con tu motivo, de modo que hermes cron runs <job_id> dirá después «cancelled — wedged on the staging API» en lugar de dejar un timeout sin explicación o un fallo seco.
Dos detalles más que importan en la práctica: una ejecución cancelada no envía aviso de fallo y no cuenta para la racha de fallos del job. Algo que detuviste a propósito no debería seguir castigando la señal de salud del job.
Cuatro puertas, una sola parada
hermes cron cancel <job>(CLI)POST /api/cron/jobs/{id}/cancel(HTTP)- la propia herramienta
cronjob_managedel agente conaction: "cancel" - la app de escritorio: un job con una ejecución en marcha muestra Stop en su cabecera; pasa por Stopping… hasta que llega la fila
cancelled, y entonces un toast Run cancelled lo confirma
Borrar un job también detiene su ejecución activa. hermes cron remove (y el borrado desde el dashboard o el escritorio, y la acción remove del agente) cancela la ejecución en curso, anotada como cancelled con el motivo «job removed while running». Antes seguía adelante y producía un resultado que nadie iba a conservar.
Cancelar un job que no tiene nada corriendo es un no-op seguro que lo indica, y una cancelación que llega cuando la ejecución ya terminó no cambia nada.
Algo que no puede hacer: deshacer
Que quede claro. Cancel no puede deshacer los efectos secundarios que la ejecución ya produjo: los archivos escritos, las APIs llamadas y los mensajes enviados se quedan. Detiene la ejecución; no restaura el mundo.
También hay un límite temporal: la interrupción ocurre en el siguiente checkpoint. Si una llamada a herramienta está atascada en una syscall no interrumpible, cuándo para depende de cuándo retorne esa llamada.
La otra mitad: qué pasa con un disparo perdido
Una vez que detienes una ejecución, la siguiente pregunta llega sola: si el job venció mientras hacías mantenimiento, ¿se pone al día cuando vuelve el gateway?
Por defecto, sí, una vez: cuando el gateway vuelve, un slot perdido dentro de la ventana de reinicio se dispara exactamente una vez, y una caída larga se colapsa en una sola ejecución en lugar de una por cada slot perdido. Puedes desactivar el comportamiento global:
hermes config set cron.catch_up_missed false
Desde la v0.22.6 un job concreto puede sobrescribir el ajuste global:
hermes cron create "0 7 * * 1-5" "Morning brief" --catch-up off # un brief viejo no sirve de nada: se omite
hermes cron edit <job> --catch-up on # ejecutar siempre una vez al volver
En el editor de jobs de escritorio esto aparece como Missed runs con tres opciones: usar el valor por defecto, ejecutar una vez al volver u omitirlas. La ventana de gracia es por defecto la mitad del periodo del job, acotada entre dos minutos y dos horas; --grace-minutes la sobrescribe por job (0 significa no ponerse al día nunca, hasta 1440). Un catch-up es visible, no silencioso: hermes cron list lo marca como ⚠ late o ⚠ catch-up after missed fire.
pause, cancel y la pausa global
Tres comandos que se mezclan; la división es limpia:
hermes cron pause <job>— conserva el job, deja de programarlo; no toca una ejecución en curso.hermes cron cancel <job>— detiene solo la ejecución actual; la programación sigue igual.hermes pause(sincron) — parada de emergencia global: no arranca ningún disparo programado en ninguna parte, pero las ejecuciones ya en marcha nunca se matan, y el trabajo vencido se pone al día trashermes resume.
Para montar una programación fiable de punta a punta, consulta nuestra guía completa de automatización con cron. Si tu problema recurrente es «el job debería haberse disparado y no lo hizo», el post del cron doctor recorre la ruta de diagnóstico, y el post sobre grace-minutes profundiza en la ventana de catch-up que mencionamos aquí.
Todo lo anterior viene incluido en el tag v0.22.6: basta con actualizar para usarlo.