Módulo 2: Robust Error Handling
7. Notificaciones en caso de fallo
Descripción
Al terminar esta lección vas a poder construir la última capa: convertir un fallo detectado en un aviso que le llega a la persona correcta, por el canal correcto, con la información suficiente para actuar sin abrir la computadora. Vas a montar los tres niveles de aviso de Terra Market, vas a escribir plantillas de mensaje a partir del payload del Error Trigger que aprendiste en la lección 6, vas a construir el workflow de barrido que cubre el punto ciego de las ramas de error, y vas a aplicar cuatro técnicas concretas contra la fatiga de alertas: agregación, deduplicación, supresión durante incidentes y ventanas horarias.
Esto importa porque es donde la mayoría de los sistemas de notificación mueren, y mueren siempre de lo mismo: de éxito aparente. Alguien conecta "avísame de cualquier ejecución fallida" a un canal de Slack, y durante tres días todo se siente maravilloso porque por fin hay visibilidad. A la tercera semana ese canal tiene cuarenta mensajes diarios, casi todos de cosas que ya se resolvieron solas, y el equipo aprendió —racionalmente— a no abrirlo. El día que llega el aviso que sí importaba, está enterrado entre treinta y nueve que no. El sistema técnicamente funcionó. Nadie se enteró de nada.
Conexión con el módulo: esta es la capa 5, la más externa, y la única donde el destinatario es un ser humano. Recibe lo que la capa 4 clasificó (lección 6) y también lo que la capa 2 apartó en dead_letter (lección 4), que —como quedó advertido— nunca dispara el error workflow global porque su ejecución termina en verde. Una frontera declarada con cuidado: la decisión de política —qué es un fallo "real" para cada workflow, quién es su dueño, con qué urgencia— está desarrollada a fondo en n8n-workflow-contracts-and-idempotency-guide, en su Módulo 6, y allí se dice explícitamente que la implementación pertenece a esta guía. Así que aquí tomamos la política como entrada y construimos la maquinaria: los canales, las plantillas, la agregación y la prueba de que el aviso llega. Y hay una segunda frontera hacia adelante: alertar por tasas —"la tasa de error subió mucho esta hora"— necesita agregar métricas en el tiempo, y eso es el Módulo 4.
Cómo avisa la escuela
Piensa en cómo la escuela de un niño se comunica con sus papás. Hay tres canales y ninguno es intercambiable.
El mensaje en la aplicación es para lo informativo: la circular de la kermés, el recordatorio de que mañana no hay clases. Lo lees cuando abres la aplicación, que puede ser hoy o pasado mañana. Si no lo lees nunca, no pasa gran cosa.
El correo es para lo que hay que atender pero no ahora mismo: la boleta de calificaciones, la solicitud de un permiso firmado. Se espera que lo veas hoy o mañana, y que hagas algo al respecto.
Y está la llamada al celular. Cuando suena el teléfono y en la pantalla dice el nombre de la escuela a media mañana, se te cae el estómago. Porque sabes, sin haber contestado, que pasó algo que no puede esperar.
Fíjate en lo que hace que ese sistema funcione: el canal comunica la urgencia antes de que leas el contenido. Sabes si tienes que soltar lo que estás haciendo con solo ver por dónde llegó el aviso.
Y fíjate ahora en cómo se rompería. Si la escuela empezara a llamarte por teléfono para avisarte de la kermés, del examen de la semana que viene y de que se acabó el papel del baño, en un mes contestarías las llamadas de la escuela con la misma prisa con que contestas una llamada de un número desconocido. Y el día del accidente de verdad, tu teléfono sonaría y tú lo dejarías para después.
Eso es exactamente la fatiga de alertas, y es la razón por la que esta lección trata mucho más sobre qué no mandar que sobre cómo mandar.
Los tres niveles, hechos canales
La política que traes de la lección 6 clasifica cada fallo en tres severidades. Ahora hay que convertirlas en algo físico. Estos son los tres niveles de Terra Market, con sus canales y sus compromisos:
| Nivel | Canal | Latencia esperada | Quién responde | Cuántos al día (objetivo) |
|---|---|---|---|---|
critical | Slack #ops-alerts con mención directa, más notificación al teléfono | Minutos, a cualquier hora | La persona de automatización (tú) | 0 o 1 |
warning | Slack #ops-daily, sin mención | Mismo día laboral | Operaciones | Menos de 10 |
info | Solo la tabla error_log, ningún canal humano | Cuando alguien quiera analizar | Nadie de forma activa | Los que sean |
La columna de la derecha es la más importante y la que casi nunca se define. Un nivel de alerta sin un objetivo de volumen no es un nivel, es una etiqueta. Si no dices cuántas alertas críticas esperas al día, no tienes forma de saber si tu sistema está calibrado o si ya empezó la fatiga.
El objetivo de 0 o 1 para critical puede sonar ambicioso, y es deliberado. Con las capas 1 a 4 haciendo su trabajo, la enorme mayoría de los fallos de Terra Market se resuelven solos o se apartan sin drama. Lo que llega a critical debería ser raro por construcción: el erp caído, el token expirado, un workflow que no arranca. Si #ops-alerts empieza a recibir cinco mensajes diarios, el problema no es el canal: es que algo de las capas anteriores no está haciendo su trabajo, o que la clasificación está mal calibrada.
Y una regla que resume todo lo anterior en una pregunta que puedes hacerte para cada alerta que diseñes:
¿Qué haría, ahora mismo, la persona que recibe esto? Si la respuesta honesta es "nada, mirar y seguir con lo suyo", entonces no es una alerta: es un registro. Mándalo a la tabla.
Anatomía de una notificación accionable
Un mensaje de alerta tiene cinco piezas. Las cinco salen del payload del Error Trigger que ya conoces, así que no hay que inventar datos nuevos: hay que decidir incluirlos.
1. Qué falló, en lenguaje de negocio. No "error en el sistema", sino "order-sync no pudo crear el pedido en el ERP". Sale de workflow.name y del contexto.
2. El identificador del caso. El order_id, el sku, lo que aplique. Sin esto, quien recibe no puede hacer nada más que abrir n8n y buscar.
3. El enlace directo a la ejecución. Sale de execution.url. Es la pieza que convierte "hay un problema" en "aquí está el problema": un clic y estás en el detalle. Si tuvieras que elegir una sola pieza de las cinco, sería esta.
4. Qué ya intentó el sistema. "Se agotaron los 3 reintentos" le dice a quien lee que esto no se va a arreglar solo. Y si execution.retryOf viene presente, decirlo: significa que ya se relanzó una vez y volvió a caer.
5. Dónde quedó guardado. "El pedido está en dead_letter" cambia por completo el nivel de estrés de quien recibe el aviso: sabe que nada se perdió y que tiene tiempo de decidir con calma.
Compara la diferencia en la práctica.
Sin diseñar:
Workflow error: order-sync
Diseñado:
🔴 order-sync · no se pudo crear el pedido en el ERP
Pedido: TM-48213 · Tienda: STORE-114
Nodo: Create order in ERP
Error: ERP API returned 503 after 3 tries
Reintento de la ejecución 91438 (segundo fallo del mismo pedido)
El pedido está guardado en dead_letter. No se perdió.
→ https://n8n.terramarket.internal/workflow/order-sync/executions/91442
El segundo mensaje no es más difícil de construir: son los mismos campos del payload puestos en una plantilla. Lo que cambia es que quien lo recibe a las tres de la mañana puede decidir, desde el teléfono, si se levanta o no. Y en la enorme mayoría de los casos la decisión va a ser "puedo esperar a mañana", que es exactamente la información que hace que valga la pena recibir la alerta.
Un detalle sobre el stacktrace: no lo mandes al canal. execution.error.stack es valiosísimo para depurar y es ruido puro en un mensaje. Guárdalo en error_log, donde estará cuando lo necesites, y deja en el canal solo execution.error.message.
Ejemplo trabajado: la rama de alerta del error-handler
Vamos a completar el error-handler que empezaste en la lección 6, añadiéndole las dos ramas de aviso.
Error Trigger ──► Code: "Classify failure" ──► Postgres: INSERT error_log
│
▼
Switch (severity)
├─ critical ──► Code: "Build alert" ──► Slack: #ops-alerts
│ (Retry On Fail: 3 × 2000)
├─ warning ───► Code: "Build alert" ──► Slack: #ops-daily
└─ info ──────► (no sale a ningún lado)
Paso 1 — El registro va primero. Ya lo viste en la lección 6 y aquí toma su forma definitiva: el INSERT en error_log está antes del Switch, no dentro de una rama. La razón es la jerarquía de garantías: escribir en tu propio Postgres es una operación local y muy confiable; mandar un mensaje a Slack depende de un servicio externo, de una credencial que puede expirar y de una red que puede fallar. Si alertaras primero, un fallo del registro te dejaría con un aviso emitido y sin rastro del caso.
Primero asegura que no se pierde, después avisa. Perder el aviso es molesto; perder el caso es el fallo que este módulo entero existe para evitar.
Paso 2 — Construir el mensaje. Un nodo Code que arma el texto a partir de los campos que clasificó el nodo anterior. Es lógica pura, sin llamadas externas, que es exactamente lo que el nodo Code de n8n 2.x permite:
// ============================================================
// Nodo: Code — "Build alert" (dentro de error-handler)
// Modo: Run Once for Each Item
//
// ENTRADA: el item clasificado que produjo "Classify failure"
// SALIDA: el mismo item más un campo alert_text listo para el canal
// POR QUÉ: separar la construcción del mensaje del envío deja el texto
// en un solo lugar y permite cambiarlo sin tocar el nodo de Slack
// ============================================================
const f = $json;
// El icono comunica la urgencia de un vistazo, antes de leer nada.
const icon = f.severity === 'critical' ? '🔴' : '🟡';
// Las líneas se construyen por separado y se filtran las vacías, para que
// una alerta sin order_id no muestre "Pedido: undefined".
const lines = [
`${icon} ${f.workflow_name} · ${f.human_summary ?? 'fallo en la ejecución'}`,
'',
f.order_id ? `Pedido: ${f.order_id}` : null,
`Nodo: ${f.failed_node}`,
`Error: ${f.error_message}`,
// retryOf solo viene cuando la ejecución fue un reintento de otra.
// Decirlo cambia la lectura: ya se intentó recuperar y volvió a caer.
f.retry_of ? `Reintento de la ejecución ${f.retry_of} (ya había fallado antes)` : null,
// Si el fallo fue en el disparador, no hay ejecución que enlazar,
// y eso hay que decirlo en vez de mandar un enlace roto.
f.trigger_failure
? 'El fallo ocurrió en el disparador: el workflow no llegó a ejecutarse.'
: null,
'',
'El caso quedó registrado en error_log.',
f.execution_url ? `→ ${f.execution_url}` : '(sin enlace: la ejecución no se creó)',
];
return {
json: {
...f,
alert_text: lines.filter((line) => line !== null).join('\n'),
},
};
Fíjate en tres decisiones de ese código, que son las que separan una plantilla que aguanta producción de una que se rompe el primer día raro:
Las líneas opcionales se filtran. Un mensaje que diga Pedido: undefined o Reintento de la ejecución null es peor que uno que no lo mencione. Construir el arreglo y filtrar los nulos resuelve eso de una vez.
El caso del fallo de disparador está contemplado. Cuando no hay execution.url, el mensaje lo dice explícitamente en vez de dejar un enlace vacío. Quien lo reciba entiende de inmediato que la situación es distinta: el workflow no está corriendo.
El texto vive separado del envío. Cambiar la redacción de las alertas no obliga a tocar el nodo de Slack ni su credencial. Es una separación pequeña que se agradece mucho a los seis meses.
Paso 3 — Enviar. Un nodo de Slack (o el de tu canal) que manda {{ $json.alert_text }}. Recuerda la restricción de n8n 2.x: la llamada al servicio la hace el nodo de la integración o un HTTP Request, nunca el nodo Code.
Paso 4 — Proteger el nodo que alerta. Activa Retry On Fail en el nodo de Slack, con Max Tries en 3 y Wait Between Tries (ms) en 2000. Sería irónico que la alerta de un fallo fallara en silencio, y como mandar un mensaje es —para el caso— una escritura cuyo peor efecto sería un mensaje duplicado, el intercambio es claramente favorable.
Qué esperar. Con esto montado, así se ve un día normal de Terra Market en los canales:
#ops-alertsrecibe cero mensajes la mayoría de los días. Cuando recibe uno, alguien lo mira en minutos, porque el canal se ganó esa reputación.#ops-dailyrecibe entre tres y ocho mensajes, que operaciones revisa por la mañana con el café.error_logacumula entre cincuenta y doscientas filas diarias, casi todasinfo, que nadie mira salvo cuando hay que investigar un patrón.
Y algo que vas a notar la primera semana: el volumen te va a decir si la clasificación está bien. Si #ops-alerts recibe cinco mensajes diarios, o la política marca como críticas cosas que no lo son, o hay un problema real y sostenido que hay que arreglar. Las dos posibilidades merecen que mires. Lo que no merece es que subas el umbral para que dejen de llegar.
El punto ciego que hay que cubrir: las ramas de error
Recuerda la advertencia de la lección 4: un item apartado por una rama de error deja la ejecución en verde, así que el error workflow global no se entera. En Terra Market eso significa que dead_letter se llena sin que nadie reciba nada.
La solución es un workflow aparte, y es de los más rentables que vas a construir: dead-letter-watch.
Schedule Trigger ──► Postgres: contar por workflow ──► If: ¿hay algo? ──► Code: resumen ──► Slack: #ops-daily
(cada hora) (última hora, status pending) │
└─ no ──► (fin, sin aviso)
La consulta. Cuenta las filas nuevas de la última hora, agrupadas por workflow y por nodo:
-- Resumen de la última hora para dead-letter-watch.
-- Agrupar es lo que convierte 47 avisos en 1.
SELECT
source_workflow,
failed_node,
COUNT(*) AS items,
MIN(created_at) AS first_seen,
MAX(created_at) AS last_seen
FROM dead_letter
WHERE created_at >= now() - interval '1 hour'
AND status = 'pending'
GROUP BY source_workflow, failed_node
ORDER BY items DESC;
El mensaje que produce:
🟡 Resumen de items apartados · última hora
inventory-update · Normalize prices 47 items
order-sync · Validate order 3 items
shipment-notify · Send email to customer 1 item
Total: 51 items en dead_letter esperando revisión.
→ https://n8n.terramarket.internal/workflow/dead-letter-watch/executions/…
Compara eso con la alternativa: cincuenta y un mensajes individuales en el canal. El resumen no solo es más soportable: es más informativo, porque muestra el patrón. Ver "47 items del mismo nodo" te dice de inmediato que hay un problema sistemático en Normalize prices, algo que cincuenta y un mensajes sueltos habrían escondido detrás del ruido.
Este es el principio general y vale para casi todo aviso de volumen: agrega por causa, no por caso.
Y una condición que hace que este workflow sirva de verdad: si no hay nada, no manda nada. El nodo If que verifica si hay filas es lo que evita el mensaje diario de "0 items apartados", que es la forma más rápida de enseñarle al equipo a ignorar el canal.
Cuatro técnicas contra la fatiga
Todo lo anterior es diseño. Estas cuatro son las técnicas concretas que aplicas cuando el volumen empieza a crecer.
Técnica 1 — Agregar por ventana de tiempo. En vez de un aviso por caso, un aviso por ventana con el conteo. Es lo que hace dead-letter-watch con su hora. Se aplica a cualquier fallo que ocurra en volumen y cuya respuesta sea la misma para todos los casos.
Cuándo usarla: cuando la acción de quien recibe no cambia por saber de qué caso concreto se trata.
Técnica 2 — Deduplicar por causa. Si el erp está caído, cuarenta ejecuciones de order-sync van a fallar con el mismo mensaje en el mismo nodo. Mandar cuarenta alertas idénticas no aporta información sobre la primera: solo hace ruido.
La implementación práctica es una marca de supresión: antes de alertar, el error workflow consulta error_log y verifica si ya hubo una alerta del mismo workflow_name + failed_node en los últimos N minutos. Si la hubo, registra pero no alerta.
Cuándo usarla: siempre que un fallo pueda repetirse en ráfaga, que es prácticamente cualquier fallo del sistema externo.
Técnica 3 — Declarar el incidente en vez de reportar los síntomas. Es la versión madura de la técnica 2. Cuando el conteo de fallos del mismo tipo cruza un umbral en poco tiempo, el mensaje cambia de forma:
🔴 INCIDENTE · order-sync · el ERP no responde
40 ejecuciones fallidas en los últimos 8 minutos.
Todas en el nodo "Create order in ERP" con 503.
Los pedidos se están guardando en dead_letter: no se está perdiendo nada.
Primer fallo: 15:32 · Último: 15:40
→ https://n8n.terramarket.internal/workflow/order-sync/executions/91442
Un solo mensaje que dice más que cuarenta. Y fíjate en la línea sobre dead_letter: le dice a quien lo recibe que puede pensar antes de correr, que es información valiosísima a las tres de la mañana.
Cuándo usarla: en workflows de volumen donde una caída del sistema externo produce ráfagas.
Dónde termina esta técnica. Contar fallos en una ventana de tiempo dentro de n8n es posible con una consulta a tu propia tabla, y para un sistema del tamaño de Terra Market alcanza. Alertar por tasas de verdad —"la tasa de error de este workflow subió respecto de su promedio de las últimas dos semanas"— exige agregar métricas en el tiempo, y eso es más natural en una herramienta de observabilidad. Es el Módulo 4. Aquí construyes la versión sencilla, que resuelve el 90% de los casos.
Técnica 4 — Ventanas horarias. No todo fallo merece el mismo tratamiento a las tres de la mañana que a las once. En Terra Market:
- El almacén empaca de 07:00 a 16:00. Un fallo de
order-synca las 03:00 tiene cuatro horas de margen antes de que estorbe a nadie. - Los picos de pedidos son a las 21:00. Un fallo ahí sí importa, aunque sea de noche.
La implementación es una condición más en la clasificación: un critical fuera del horario sensible puede degradarse a warning si —y solo si— los casos están a salvo en dead_letter. Esa condición es innegociable: solo puedes posponer un aviso si estás seguro de que nada se está perdiendo mientras tanto.
Cuándo usarla: con cuidado, y siempre con la condición anterior verificada. Una ventana horaria mal puesta es la forma más elegante de descubrir un desastre por la mañana.
Las cuatro técnicas comparten un principio: el volumen de alertas debe ser proporcional a la cantidad de decisiones humanas necesarias, no a la cantidad de fallos. Cuarenta pedidos fallidos por una misma causa requieren una decisión humana. Merecen un mensaje.
Probar que el aviso llega
Un canal de alertas es infraestructura que solo se ejercita cuando algo se rompe, y por eso se degrada en silencio. La credencial de Slack expira. Alguien archiva el canal. El webhook del servicio de mensajería cambia de URL. Y te enteras el peor día posible.
El procedimiento, que se apoya en el error-handler-test que montaste en la lección 6:
Una vez al mes, publica error-handler-test durante cinco minutos, deja que falle una vez, y verifica los cuatro eslabones de la cadena:
- ¿Se ejecutó
error-handler? - ¿Se escribió la fila en
error_log? - ¿Llegó el mensaje al canal correcto según la severidad?
- ¿El mensaje contiene el enlace y el enlace funciona?
Los tres primeros fallan poco. El cuarto falla más de lo que uno esperaría, casi siempre por lo mismo: la instancia cambió de dominio, o las ejecuciones dejaron de guardarse y execution.url viene vacío. Un enlace roto en una alerta convierte un aviso accionable en uno que obliga a investigar desde cero.
Y un hábito complementario que cuesta nada: cuando ocurra un incidente real, después de resolverlo, dedica dos minutos a preguntarte si la alerta te dio lo que necesitabas. Si tuviste que abrir tres pestañas para entender qué pasaba, a la plantilla le falta una pieza. Ese es el momento de agregarla, con el caso fresco.
Errores comunes
Alertar de cada ejecución fallida (conceptual). Qué pasa: se conecta "notifícame de todo fallo" a un canal, con la mejor intención de no perderse nada. En dos semanas llegan decenas de mensajes diarios, casi todos de fallos transitorios que ya se resolvieron antes de que nadie los leyera, y el equipo deja de abrir el canal. Por qué pasa: es la opción más fácil de configurar y la que se siente más segura, porque cubrir todo parece mejor que cubrir poco. Cómo detectarlo: cuenta cuántas alertas recibiste esta semana y qué fracción llevó a que alguien hiciera algo. Si la mayoría no requirieron ninguna acción, la fatiga ya empezó. Cómo corregirlo: aplica la pregunta de esta lección a cada tipo de alerta —"¿qué haría ahora mismo quien recibe esto?"— y manda a la tabla todo lo que responda "nada". Las capas 1 a 4 ya resolvieron la mayoría de los fallos; si tu canal recibe mucho volumen, es señal de que no estás confiando en ellas.
Mandar el stacktrace al canal (práctico). Qué pasa: para que la alerta sea "completa", se incluye execution.error.stack en el mensaje. El resultado es un bloque de treinta líneas de traza técnica en el teléfono de alguien a las tres de la mañana, que oculta las dos líneas que sí importaban. Por qué pasa: más información parece siempre mejor, y el stack está ahí, disponible, gratis. Cómo detectarlo: mira tus alertas en un teléfono, no en la computadora. Si hay que hacer scroll para llegar al identificador del caso, sobra contenido. Cómo corregirlo: manda execution.error.message al canal y guarda execution.error.stack en error_log. Quien necesite la traza va a estar frente a una computadora cuando la necesite; quien recibe la alerta necesita decidir si se levanta.
Poner una ventana horaria sin verificar que nada se pierde (conceptual). Qué pasa: para no recibir avisos de madrugada, se configura que los critical fuera de horario esperen a la mañana. Suena razonable y funciona bien varias semanas. Una noche, el fallo que se pospone es uno donde los pedidos no se están guardando en dead_letter —porque el nodo que falló estaba antes de la rama de error—, y por la mañana hay ocho horas de pedidos perdidos. Por qué pasa: la ventana horaria se diseña pensando en el caso conocido, y los fallos que importan casi nunca son el caso conocido. Cómo detectarlo: para cada tipo de fallo que pospones, pregúntate qué está pasando con los datos durante las horas en que nadie mira. Si no puedes responderlo con certeza, no lo pospongas. Cómo corregirlo: haz que la condición de posponer dependa explícitamente de una garantía verificable —"el item quedó en dead_letter"— y no de la hora sola. Sin esa garantía, el fallo interrumpe aunque sean las tres de la mañana.
Ejercicios
Ejercicio 1 — Clasifica y encamina. Para cada una de estas seis situaciones de Terra Market, di a qué nivel corresponde (critical, warning, info), por qué canal sale, y si además aplicarías alguna de las cuatro técnicas antifatiga:
(a) La API del carrier da un 503 y el reintento lo resuelve en el segundo intento.
(b) Un pedido llega con total: 0 y el nodo Reject: zero total lo detiene.
(c) El token del erp expiró y order-sync lleva doce ejecuciones seguidas fallando.
(d) Tres sku con precio vacío se apartan en dead_letter durante una corrida de inventory-update.
(e) shipment-notify no consiguió mandar un correo porque el customer_email era inválido.
(f) inventory-update no ha tenido ninguna ejecución en las últimas tres horas.
Ver solución
(a) Ni siquiera info: no llega al error workflow. El reintento tuvo éxito, así que la ejecución terminó en verde y nunca hubo un fallo desde el punto de vista de n8n. Es la capa 1 haciendo exactamente lo que debe: resolver sin molestar a nadie. Si esto llegara a un canal, tendrías fatiga garantizada.
(b) info, solo a error_log. Es un rechazo deliberado: el sistema funcionando, no fallando. El nodo se llama Reject: zero total, así que la clasificación de la lección 6 lo detecta por el prefijo. Vale la pena revisar la tabla de vez en cuando: si los rechazos por total: 0 se disparan, hay un bug en storefront que alguien debe arreglar.
(c) critical, a #ops-alerts con mención. Es un fallo de configuración que no se cura solo y que está deteniendo el negocio: cada minuto son pedidos que no entran. Y aplica claramente la técnica 3: doce ejecuciones fallidas con el mismo mensaje en el mismo nodo deben producir un mensaje de incidente, no doce alertas.
(d) No llega al error workflow —la ejecución terminó en verde— así que lo cubre dead-letter-watch con su barrido horario. Sale a #ops-daily agregado con el resto: técnica 1. Tres sku no merecen ni un mensaje propio.
(e) Depende de cómo lo manejaste, y esa es la enseñanza. Si el nodo tiene rama de error, el item se aparta y lo recoge dead-letter-watch agregado. Si lo dejaste en Stop Workflow, la ejecución muere en rojo, llega al error workflow y se clasifica como warning según la política de shipment-notify. Las dos son defendibles; lo que no lo es sería que fuera critical: un cliente sin aviso es molesto, no urgente, y con 2.500 ejecuciones diarias alertar críticamente por cada correo fallido mataría el canal en un día.
(f) Ninguna de las anteriores: el error workflow no se entera. No hay ejecuciones fallidas, hay ausencia de ejecuciones. Es el punto ciego 3 de la lección 6, y lo cubre un health check del Módulo 4. Con un matiz que vale la pena notar: inventory-update corre cada 15 minutos, así que tres horas sin ejecuciones son doce corridas perdidas y es inequívocamente un problema. En order-sync, tres horas sin ejecuciones a las 04:00 podrían ser normales. La ausencia solo es señal cuando la comparas contra el ritmo esperado de ese workflow.
Por qué funciona: de las seis situaciones, solo una merece interrumpir a alguien, dos se agregan, una se registra en silencio, una no llega siquiera al sistema de errores y otra necesita una herramienta distinta. Ese reparto es el objetivo de toda la lección: que el canal crítico sea tan raro que, cuando suene, nadie dude en atenderlo.
Ejercicio 2 — Reescribe la alerta. Esta es la alerta que Terra Market tenía antes de esta lección. Reescríbela aplicando las cinco piezas de una notificación accionable, inventando los datos que falten a partir del payload del Error Trigger:
Error in workflow order-sync
Error: Request failed with status code 503
Ver solución
Una versión reescrita:
🔴 order-sync · no se pudo crear el pedido en el ERP
Pedido: TM-48213 · Tienda: STORE-114
Nodo: Create order in ERP
Error: ERP API returned 503 after 3 tries
El pedido está guardado en dead_letter: no se perdió.
Si el ERP sigue caído, los siguientes también se guardarán.
→ https://n8n.terramarket.internal/workflow/order-sync/executions/91442
Qué se agregó y de dónde sale cada pieza:
| Pieza | De dónde sale |
|---|---|
| "no se pudo crear el pedido en el ERP" | Traducción a lenguaje de negocio del workflow.name + lastNodeExecuted |
TM-48213 | Del item que se estaba procesando |
STORE-114 | Del mismo item; sirve para correlacionar si todos vienen de la misma tienda |
Create order in ERP | execution.lastNodeExecuted |
| "after 3 tries" | Del mensaje del error; dice que la capa 1 ya se agotó |
| "está guardado en dead_letter" | Tu conocimiento del diseño del workflow |
| El enlace | execution.url |
Fíjate en la línea que más valor aporta y que no está en ningún campo del payload: "El pedido está guardado en dead_letter: no se perdió". Esa frase no la calcula nadie; la escribes tú porque conoces tu sistema. Y es la que decide si quien recibe la alerta a las tres de la mañana se levanta corriendo o respira y la atiende con calma. Una alerta que informa del daño contenido es tan valiosa como una que informa del daño.
Y una línea que no agregué a propósito: el stacktrace. Va a error_log, no al canal.
Por qué funciona: el mensaje original obliga a abrir n8n para saber cualquier cosa —qué pedido, si se perdió, si hay más—. El reescrito permite decidir desde el teléfono. Y el costo de la diferencia es una plantilla que escribes una sola vez y sirve para siempre.
Ejercicio 3 — Diseña la política de notificación de un workflow nuevo. Terra Market lanza refund-process, que procesa devoluciones: recibe la solicitud, valida que el pedido exista y esté dentro del plazo, devuelve el dinero por la API del procesador de pagos, y avisa al cliente. Corre unas 40 veces al día. Diseña: qué fallos son critical, warning e info; qué canal para cada uno; qué técnicas antifatiga aplicarías; y cómo probarías que las alertas llegan.
Ver solución
La clasificación, nodo por nodo:
-
Validación fallida (el pedido no existe, está fuera de plazo, ya se devolvió):
info. Son rechazos deliberados del estilo de la lección 5, y son el sistema funcionando. Nodos nombradosReject: order not found,Reject: outside window,Reject: already refunded, para que el prefijo los clasifique automáticamente. Aerror_log, sin canal. -
Fallo transitorio del procesador de pagos que el reintento resuelve: no llega a ninguna parte. La ejecución termina en verde. Correcto.
-
Fallo del procesador tras agotar reintentos:
critical, siempre, a#ops-alertscon mención a finanzas. Es dinero que el cliente espera y que no salió. Con solo 40 ejecuciones diarias, el volumen no es un riesgo: alertar por cada caso es perfectamente sostenible y aquí el caso concreto sí importa, porque cada devolución es una persona esperando su dinero. -
Fallo al avisar al cliente, con el reembolso ya ejecutado:
warning, a#ops-daily. El dinero salió, que es lo importante; falta el aviso, que se puede reenviar a mano. Con una condición: que el caso quede endead_letterpara poder reenviarlo. -
Fallo de disparador:
critical. Sirefund-processno está corriendo, las devoluciones se acumulan sin que nadie lo note.
Las técnicas antifatiga:
- Deduplicación (técnica 2): sí. Si el procesador de pagos se cae, veinte solicitudes van a fallar igual. Una alerta de incidente, no veinte.
- Agregación (técnica 1): no para los
critical. Con 40 ejecuciones diarias y dinero de por medio, cada caso individual merece su mensaje. Sí para loswarningde avisos no enviados, que se pueden resumir. - Ventanas horarias (técnica 4): no, y esta es la decisión que quiero justificar. Es tentador decir "una devolución puede esperar a la mañana". Pero el fallo aquí deja a un cliente sin su dinero y sin explicación, y el estado del sistema puede ser ambiguo —¿el pago salió y solo se perdió la confirmación?—. Ese tipo de ambigüedad es exactamente lo que no debe esperar ocho horas sin que alguien lo mire. Y con cero o una alerta crítica al día, el costo de no poner ventana es despreciable.
- Incidente declarado (técnica 3): sí, si el procesador se cae.
Cómo probarlo:
- Un
refund-process-testdesechable con unStop and Errorque produzca un fallo clasificado comocritical, publicado unos minutos. - Verificar los cuatro eslabones: se ejecutó
error-handler, se escribió enerror_log, llegó a#ops-alertscon la mención, y el enlace abre la ejecución correcta. - Repetir una vez al mes, y siempre después de tocar la credencial de Slack o de cambiar el dominio de la instancia.
- Probar además el caso del fallo de disparador —despublicando algo a propósito, por ejemplo— para verificar que la plantilla no manda un enlace roto cuando no hay
execution.url.
Por qué funciona: el ejercicio muestra que la política de notificación depende de tres variables, no de una sola: la gravedad de negocio (aquí es dinero), el volumen (aquí es bajo) y la ambigüedad del estado (aquí es alta). Con volumen bajo y gravedad alta, alertar caso por caso es correcto. Con volumen alto y gravedad media —shipment-notify— sería un desastre. No hay una política universal: hay una decisión por workflow, tomada sobre esas tres variables.
Resumen y siguiente paso
En esta lección construiste la última capa. Con la imagen de los tres canales de la escuela viste el principio que la gobierna: el canal comunica la urgencia antes de que leas el contenido, y ese sistema se rompe en cuanto empiezas a llamar por teléfono para avisar de la kermés. Definiste los tres niveles de Terra Market con sus canales y —lo más importante— con un objetivo de volumen para cada uno, porque un nivel sin objetivo de volumen es una etiqueta y no un nivel. Aprendiste las cinco piezas de una notificación accionable —qué falló en lenguaje de negocio, el identificador, el enlace a la ejecución, qué ya intentó el sistema, y dónde quedó guardado— y construiste la rama de alerta del error-handler con una plantilla que filtra las líneas vacías y contempla el caso del fallo de disparador. Cubriste el punto ciego de las ramas de error con dead-letter-watch, que agrega por causa y no manda nada cuando no hay nada. Y aplicaste las cuatro técnicas antifatiga: agregar por ventana, deduplicar por causa, declarar el incidente en vez de reportar los síntomas, y las ventanas horarias con su condición innegociable de que nada se esté perdiendo mientras nadie mira.
Antes de avanzar deberías poder: decidir a qué nivel corresponde un fallo concreto y por qué canal sale; escribir una alerta que permita decidir desde el teléfono; y explicar por qué un canal crítico con cinco mensajes diarios está roto aunque los cinco sean ciertos.
Con esto tienes las cinco capas completas: el reintento que cura solo, la rama que aparta, el fallo que provocas, la red que recoge, y el aviso que llega. Lo que falta es lo único que garantiza que todo esto es real en vez de decorativo: probarlo. La lección 8 es el proyecto del módulo. Vas a tomar order-sync —el workflow más crítico de Terra Market— y le vas a poner las cinco capas de punta a punta: reintentos con su verificación previa, ramas de error conectadas a dead_letter, validaciones de negocio con mensajes de nivel 4, el error-handler asignado, y las alertas calibradas. Y después vas a hacer la parte que casi nadie hace: romperlo a propósito, seis veces, una por cada tipo de fallo, y verificar con tus ojos que cada uno terminó donde debía.
Recursos
- Error Trigger — n8n Docs — la fuente de los campos que llenan tus plantillas de alerta:
execution.url,execution.error.message,execution.lastNodeExecuted,execution.retryOfyworkflow.name. - Handle errors gracefully — n8n Docs — el marco del manejo de errores donde la notificación es la última pieza, con la nota de que los enlaces a ejecuciones dependen de que estas se guarden en la base de datos.
- Work with nodes — n8n Docs — la pestaña Settings donde activas
Retry On Failen el propio nodo de notificación, para que la alerta de un fallo no falle en silencio. - Slack node — n8n Docs — el nodo que envía el mensaje al canal; recuerda que en n8n 2.x la llamada la hace el nodo de la integración o un HTTP Request, nunca el nodo Code.
- Postgres node — n8n Docs — el nodo con el que se escribe
error_logantes de alertar y con el quedead-letter-watchconsulta el resumen por hora.