Módulo 6: Reintentos, alertas y recuperación

5. Error workflows y colas de mensajes muertos

Descripción

Al terminar esta lección vas a poder construir dos piezas que trabajan juntas: un error workflow central —con el nodo Error Trigger como primer nodo— que captura en un solo lugar cualquier fallo de cualquier workflow del sistema, y una cola de mensajes muertos —una tabla dead_letter en el mismo Postgres del ledger— donde el item que falló tras agotar todos sus reintentos se aparta con su contexto completo, para que no se pierda y se pueda reprocesar a mano. Vas a ver cómo se configura el error workflow en los settings de un workflow, qué payload exacto recibe el Error Trigger cuando algo falla, cómo ese payload se convierte en la decisión de alertar o solo registrar que diseñaste en la lección 4, y cómo se reprocesa un item apartado sin volver a duplicar nada.

Esto importa porque es donde las decisiones de las lecciones anteriores se vuelven maquinaria que corre sola. Definiste que un reembolso atascado debe alertar a finanzas (lección 4); aquí está el nodo que atrapa ese fallo y manda la alerta. Definiste que un pedido mal formado no debe perderse; aquí está la tabla que lo guarda. Sin esta pieza, un fallo terminal termina en una ejecución marcada en rojo que alguien tiene que descubrir de casualidad, y el item que la causó se evapora. Con esta pieza, cada fallo del sistema pasa por un solo embudo que lo registra, lo enruta según su gravedad y —si no se pudo procesar— lo aparta intacto. Es la red debajo del trapecio: puede que no la uses seguido, pero es la diferencia entre un tropiezo y una caída.

Conexión con el módulo: esta lección construye la infraestructura que ejecuta las decisiones de la lección 4 y atrapa lo que las lecciones 2 y 3 no pudieron resolver —el reintento que se agotó, la compensación que también falló—. Se apoya en el ledger del Módulo 4, porque la cola de mensajes muertos es otra tabla del mismo Postgres y sigue las mismas ideas de dedup e idempotencia. La lección 6 usará lo que esta cola guarda: el contexto de un fallo apartado es, precisamente, lo que vas a cargar en el motor de replay para reproducir un bug. Recuerda una restricción que sigue en pie: escribir en Postgres se hace con el nodo Postgres, no desde un nodo Code, y llamar a una API de alerta se hace con el nodo HTTP Request.

El nodo Error Trigger: un solo embudo para todos los fallos

Empecemos por la pieza que n8n te da lista: el nodo Error Trigger.

Un Error Trigger es un tipo especial de nodo disparador. En vez de dispararse por un webhook o por un horario, se dispara cuando otro workflow falla. Es el primer nodo de un workflow aparte —el error workflow— cuyo único trabajo es reaccionar a los fallos de los demás. Cuando un workflow cualquiera del sistema termina en error, n8n arranca el error workflow y le entrega, por el Error Trigger, todos los detalles de lo que salió mal.

La analogía es la centralita de emergencias de un edificio. Cada departamento tiene sus propios sensores, pero todos están cableados a un solo panel central en la portería. No importa en qué piso se dispare una alarma: la señal llega al mismo lugar, con la información de qué piso y qué tipo de alarma fue, y desde ahí se decide a quién llamar. El Error Trigger es esa portería: un solo lugar al que llegan los fallos de todos los workflows, con la información de cuál falló y por qué.

La ventaja de tener un error workflow central en vez de manejar los errores dentro de cada workflow es la misma que la de cualquier embudo: un solo lugar que mantener, una sola lógica de "cómo reacciono a un fallo", en vez de repetir esa lógica —y sus inevitables inconsistencias— en cada uno de los cuatro workflows de Cumbre. Cambias la política en un lado y aplica a todo el sistema.

Conviene saber que las dos formas de manejar errores no se excluyen, y cada una tiene su lugar. El manejo dentro del workflow —con la salida de error del nodo que viste en la lección 2, que enruta el item fallido por su propia rama sin detener el flujo— sirve para los fallos que quieres resolver en el momento, como disparar una compensación (lección 3). El error workflow central sirve para los fallos que ya detuvieron la ejecución y que hay que registrar, clasificar y apartar después. Un sistema maduro usa los dos: la salida de error para reaccionar en caliente a lo que se puede resolver, y el Error Trigger central como la red que atrapa todo lo que llegó hasta el final sin resolverse. No es uno u otro; es cada uno en su nivel.

Cómo se configura un error workflow

Son dos pasos, y conviene tener claro cada uno porque el orden importa.

Paso 1 — Crear el error workflow. Creas un workflow nuevo cuyo primer nodo es un Error Trigger. Le pones un nombre reconocible —para Cumbre, cumbre-error-handler— y lo guardas. Ese Error Trigger no necesita configuración: su trabajo es recibir el fallo. Todo lo que venga después —registrar, decidir gravedad, alertar, apartar en la cola— lo diseñas tú.

Paso 2 — Decirle a cada workflow que use ese error workflow. En cada workflow del sistema abres sus opciones —Options > Settings— y en el campo Error workflow eliges cumbre-error-handler. Con eso, cuando ese workflow falle, n8n arrancará el error workflow y le pasará el fallo.

Tres detalles de la documentación oficial que conviene saber, porque ahorran confusión:

  • Un workflow que contiene un nodo Error Trigger se usa a sí mismo como su propio error workflow por defecto. Es decir, si el cumbre-error-handler fallara, no necesitas configurarle otro; ya sabe manejarse. Y no necesitas activarlo ni publicarlo para eso.
  • No puedes probar un error workflow ejecutándolo a mano. El Error Trigger solo se dispara cuando un workflow falla en una ejecución automática. Si lo corres manualmente para probar, no pasa nada, porque no hubo un fallo real que lo disparara. Esto confunde muchísimo la primera vez: parece que "no funciona", cuando en realidad está esperando un fallo de verdad.
  • Conviene asignar el error workflow a todos los workflows del sistema, para que ninguno falle en silencio. Es una casilla por workflow, y es de las que más tranquilidad dan por lo poco que cuestan.

El payload: qué recibe el Error Trigger cuando algo falla

Aquí está la parte concreta. Cuando un workflow falla, el Error Trigger recibe un item con una estructura precisa, y necesitas conocerla porque de ahí sacas todo lo que vas a registrar y alertar.

Según la documentación oficial, cuando falla un nodo cualquiera del workflow (no el disparador), el payload tiene esta forma:

{
  "execution": {
    "id": "231",
    "url": "https://tu-instancia/workflow/abc/executions/231",
    "retryOf": "230",
    "error": {
      "message": "descripción del error",
      "stack": "traza técnica del error"
    },
    "lastNodeExecuted": "Place credit hold",
    "mode": "trigger"
  },
  "workflow": {
    "id": "abc",
    "name": "check-credit"
  }
}

Desmenucemos los campos que vas a usar de verdad:

  • workflow.name y workflow.id: qué workflow falló. Es lo primero que necesitas para decidir la política —recuerda que en la lección 4 la política era por workflow—.
  • execution.lastNodeExecuted: el nombre del nodo donde se detuvo la ejecución. Te dice dónde falló: Place credit hold, issue-refund, el nodo de validación. Es oro para diagnosticar.
  • execution.error.message: la descripción del error, en texto legible. Es lo que va en la alerta y en el registro.
  • execution.error.stack: la traza técnica, más detallada. Útil para depurar, pero normalmente no para la alerta —es ruido para un humano apurado—.
  • execution.id y execution.url: el identificador y el enlace directo a la ejecución que falló. Esto es lo que convierte una alerta en una alerta útil (lección 4): el dueño hace clic y llega directo al detalle. Ojo con un matiz de la documentación: estos dos campos requieren que la instancia esté guardando las ejecuciones en la base de datos; si el fallo ocurre en el propio disparador, pueden no estar.
  • execution.retryOf: si esta ejecución fue un reintento de otra, aquí está el id de la original. Solo aparece en reintentos.

Hay una segunda forma del payload, para cuando el fallo ocurre en el nodo disparador mismo —antes de que la ejecución arranque de verdad—. En ese caso, en lugar de execution viene un objeto trigger con el error, y workflow con su id y nombre. Es un caso de borde, pero conviene saber que existe para que tu error workflow no reviente al recibir un payload con una forma distinta a la que esperaba. En la práctica, lees los campos con cuidado y toleras que alguno falte.

La cola de mensajes muertos: no perder lo que no se pudo procesar

Ahora la segunda pieza, y la que le da nombre a la lección.

Una cola de mensajes muertos (en inglés, dead-letter queue) es un lugar donde se apartan los mensajes o items que, después de todos los intentos, no se pudieron procesar —para no perderlos y poder revisarlos después—. El nombre viene de la mensajería: una "carta muerta" es una carta que no se pudo entregar ni devolver al remitente, y que la oficina de correos aparta en un cajón especial en vez de tirarla a la basura.

Esa imagen es exactamente el concepto. Cuando un pedido de Cumbre falla de forma terminal —agotó sus reintentos, su compensación también falló, o llegó tan mal formado que no se pudo ni evaluar—, no lo descartas y no lo dejas dando vueltas rompiendo cosas. Lo pones en el cajón de las cartas muertas: la tabla dead_letter, en el mismo Postgres del ledger. Ahí espera, intacto y con su contexto, hasta que un humano lo revise y decida qué hacer —reprocesarlo, corregirlo, o descartarlo a conciencia—.

La diferencia entre un sistema con cola de mensajes muertos y uno sin ella es brutal, y se ve en el peor momento. Sin la cola, un pedido que falló terminal simplemente desaparece: la ejecución quedó en rojo, y si nadie la vio, el pedido de Luna Coffee nunca se procesó y nadie lo sabe hasta que Luna llama enojada. Con la cola, ese mismo pedido está guardado, etiquetado con por qué falló y cuándo, esperando a que alguien lo atienda. Nada se pierde en silencio: esa es la promesa entera del módulo, hecha tabla.

La tabla dead_letter

Veamos qué columnas necesita, y por qué cada una. Recuerda que crear y escribir esta tabla se hace con SQL a través del nodo Postgres, sobre el Postgres que trae el Starter Kit.

-- La cola de mensajes muertos de Cumbre.
-- Vive en el mismo Postgres que el run_ledger del Módulo 4.
CREATE TABLE dead_letter (
  id              BIGSERIAL PRIMARY KEY,
  order_id        TEXT,          -- para reencontrar el pedido y correlacionar
  source_workflow TEXT,          -- qué workflow falló: 'issue-refund', etc.
  failed_node     TEXT,          -- lastNodeExecuted: dónde se detuvo
  error_message   TEXT,          -- error.message: por qué falló, legible
  execution_id    TEXT,          -- execution.id: enlace a la ejecución para el replay
  payload         JSONB,         -- el item completo que se intentaba procesar
  severity        TEXT,          -- 'critical' | 'warning' (de la lección 4)
  status          TEXT DEFAULT 'pending',  -- 'pending' | 'reprocessed' | 'discarded'
  created_at      TIMESTAMPTZ DEFAULT now()
);

Fíjate en dos columnas que hacen todo el trabajo pesado:

payload (el item completo). Esta es la razón de ser de la cola. Guardas el pedido entero, tal como se intentaba procesar cuando falló, no solo su order_id. Así, cuando alguien lo reprocese, tiene todos los datos originales sin depender de que el evento original siga disponible en algún lado. Es la carta completa dentro del cajón, no una nota de que "llegó una carta".

status. Marca en qué estado está cada item apartado: pending (esperando revisión), reprocessed (ya se volvió a procesar con éxito), discarded (alguien decidió a conciencia que no se procesa). Sin esta columna, no sabrías qué de la cola ya se atendió y qué sigue esperando, y volverías a procesar cosas ya resueltas.

Ejemplo trabajado: el error workflow de Cumbre de punta a punta

Armemos el cumbre-error-handler completo, juntando el Error Trigger, la decisión de la lección 4 y la cola. Este es el workflow al que apuntan los cuatro workflows del sistema en sus settings.

Error Trigger  ──►  Code: "Classify failure"  ──►  Switch (por severidad)
                                                      ├─ critical ─► HTTP Request: alertar
                                                      │                        │
                                                      │                        ▼
                                                      └─ warning ──────► Postgres: INSERT dead_letter

Nodo 1 — Error Trigger. Recibe el fallo. No se configura; solo entrega el payload.

Nodo 2 — Code: "Classify failure". Lee el payload y decide la gravedad según la política de la lección 4. Es un nodo Code puro —solo lógica, sin HTTP ni Postgres—:

// ============================================================
// Nodo: Code — "Classify failure" (en cumbre-error-handler)
// Modo: Run Once for Each Item
//
// ENTRADA:  el payload del Error Trigger
// SALIDA:   un item aplanado y clasificado, listo para registrar/alertar
// POR QUÉ:   traducir el fallo técnico a la decisión de negocio de la
//            lección 4 (qué gravedad, qué workflow, qué contexto)
// ============================================================

const payload = $json;

// Los campos pueden faltar si el fallo fue en el disparador: se leen
// con cuidado, tolerando ausencias, para que el handler no reviente.
const workflowName = payload.workflow?.name ?? 'unknown';
const failedNode   = payload.execution?.lastNodeExecuted ?? 'unknown';
const errorMessage = payload.execution?.error?.message ?? 'sin mensaje';
const executionId  = payload.execution?.id ?? null;

// El order_id viaja dentro de los datos que el workflow estaba procesando.
// Según cómo lo exponga cada workflow, puede estar en distintos lugares;
// aquí se intenta la ubicación esperada y se deja null si no aparece.
const orderId = payload.execution?.error?.context?.order_id ?? null;

// La política de la lección 4, hecha código: issue-refund es crítico
// siempre; el resto es advertencia salvo que el sistema esté caído.
const isRefund = workflowName === 'issue-refund';
const severity = isRefund ? 'critical' : 'warning';

return {
  json: {
    order_id: orderId,
    source_workflow: workflowName,
    failed_node: failedNode,
    error_message: errorMessage,
    execution_id: executionId,
    severity,                       // decide por dónde sale del Switch
  },
};

Nodo 3 — Switch por severidad. Un nodo Switch enruta según el campo severity: los critical van a la rama de alerta, y todos —críticos y advertencias— terminan en la cola. (En el diagrama simplifiqué; en la práctica el crítico se alerta y además se guarda en la cola, porque alertar no exime de no perder el item).

Nodo 4a — HTTP Request: alertar (solo la rama crítica). Llama a la API del canal de alertas de Cumbre —recuerda, la llamada es un HTTP Request, no un nodo Code— con un mensaje útil según la lección 4: qué falló, el order_id, el enlace a la ejecución, qué ya se intentó. Este nodo lleva su propio Retry On Fail: sería irónico que la alerta de un fallo fallara en silencio.

Nodo 4b — Postgres: INSERT en dead_letter. Guarda el item en la cola con todo su contexto. Un INSERT con los campos que preparó el nodo Code.

Un detalle de orden que vale la pena pensar: conviene que el INSERT en dead_letter ocurra antes —o al menos con la misma prioridad— que el intento de alertar. La razón es la jerarquía de garantías. Guardar en Postgres es una escritura local y confiable que casi nunca falla; mandar una alerta depende de un servicio externo que sí puede estar caído. Si alertaras primero y el guardado quedara para después, un fallo del guardado te dejaría con una alerta emitida pero sin el item en la cola —sabes que algo falló, pero perdiste el pedido—. Guardando primero, aunque la alerta falle, el item está a salvo y siempre se puede alertar después. La regla general: primero asegura que no se pierde, después avisa. Perder el aviso es molesto; perder el pedido es el fallo que este módulo entero existe para evitar.

Qué esperar de punta a punta. Cuando issue-refund agota sus reintentos intentando emitir un reembolso, la ejecución falla, n8n arranca cumbre-error-handler, el Code clasifica el fallo como critical, el Switch lo manda a la rama de alerta y a la cola: finanzas recibe una alerta con el order_id y el enlace a la ejecución, y el pedido queda guardado en dead_letter con status = 'pending'. Cuando en cambio order-triage rechaza un pedido mal formado, el mismo handler lo clasifica como warning, no alerta a nadie, y lo guarda en la cola para revisión en el día. Un solo error workflow, dos comportamientos distintos, gobernados por la política que diseñaste en la lección 4.

Reprocesar desde la cola sin duplicar

Guardar el item es media historia; la otra media es sacarlo. Y aquí vuelve, por última vez en el módulo, la regla de oro: reprocesar un item de la cola de mensajes muertos re-ejecuta sus efectos, así que tiene que ser idempotente.

Piénsalo. Un reembolso terminó en la cola porque la API de pagos estaba caída. Horas después, la API vuelve, y alguien reprocesa el item. Si la API en realidad había recibido la petición original antes de caerse —el mundo B de la lección 2—, reprocesar sin idempotencia emite un segundo reembolso. La cola de mensajes muertos no es una excepción a las reglas del módulo: reprocesar es, exactamente, un reintento manual y tardío, y juega con las mismas reglas que cualquier reintento.

Por eso el reproceso se apoya en las mismas dos protecciones de siempre: la clave de idempotencia en la petición (para que la API reconozca el duplicado) y el estado —el ledger del Módulo 4 y la columna status de la cola— para no reprocesar dos veces lo mismo. El flujo de reproceso es:

  1. Un humano (o un workflow de mantenimiento) lee los items pending de la cola.
  2. Para cada uno, re-lanza el procesamiento con la misma clave de idempotencia que tenía originalmente —por eso el payload completo se guardó: la clave está ahí—.
  3. Si tiene éxito, marca el item como reprocessed. Si el humano decide que no se procesa, lo marca como discarded.

Un detalle que cierra el círculo con el Módulo 2: la clave de idempotencia tiene que derivarse de datos estables del pedido, no del momento del reproceso. Si la clave dependiera de la hora, reprocesar generaría una clave nueva y la protección se caería. Por eso, desde el Módulo 2, las claves se derivan del order_id y del tipo de operación: sobreviven a un reproceso que ocurre horas o días después del fallo original. Todo el módulo se sostiene en esa decisión.

Errores comunes

Intentar probar el error workflow ejecutándolo a mano (práctico). Qué pasa: se termina de armar cumbre-error-handler, se presiona ejecutar para probarlo, y no pasa nada útil —el Error Trigger no tiene un fallo real que procesar—. Se concluye que "no funciona" y se pierde una tarde revisando algo que está bien. Por qué pasa: es el reflejo natural, y la documentación advierte justo esto: el Error Trigger solo se dispara con un fallo de una ejecución automática. Cómo detectarlo: si estás ejecutando el error workflow directamente y esperando ver el manejo del error, estás en este caso. Cómo corregirlo: prueba provocando un fallo real en un workflow que tenga asignado este error workflow —por ejemplo, un nodo que apunte a una URL inválida a propósito—, deja que falle en una ejecución, y observa cómo se dispara el handler. Esa es la única forma de verlo trabajar de verdad.

Guardar solo el order_id en la cola, no el pedido completo (conceptual). Qué pasa: para "ahorrar espacio", se guarda en dead_letter únicamente el identificador del pedido, no su payload entero. Cuando alguien va a reprocesar, resulta que el evento original ya no está disponible —el webhook no se repite— y no hay forma de reconstruir los datos con los que se intentaba procesar. El item guardado es inútil. Por qué pasa: guardar todo se siente redundante si el pedido "ya está en algún lado". Cómo detectarlo: pregúntate "si tuviera que reprocesar esto dentro de tres días, ¿tengo aquí todo lo que necesito, sin depender de nada externo?". Si la respuesta es no, te falta contexto. Cómo corregirlo: guarda el payload completo del item. La cola de mensajes muertos existe para no depender de que el origen siga disponible; guardar solo una referencia rompe justo esa garantía.

Reprocesar la cola sin idempotencia (conceptual). Qué pasa: se junta un lote de reembolsos que fallaron por una caída de la API de pagos, la API vuelve, y se reprocesan todos "para ponerse al día", sin verificar claves de idempotencia. Algunos de esos reembolsos se habían emitido antes de la caída —solo se había perdido la confirmación—, y ahora se emiten de nuevo: dinero que sale dos veces. Por qué pasa: la cola se siente como "lo que quedó pendiente", y pendiente sugiere "no se hizo". Pero un fallo terminal no garantiza que el efecto no ocurrió; garantiza que no se confirmó. Cómo detectarlo: antes de reprocesar en masa, pregúntate si cada item lleva la misma clave de idempotencia original y si la API la respeta. Cómo corregirlo: reprocesa con la clave original guardada en el payload, y consulta el ledger para saltarte lo que ya está marcado como hecho. Reprocesar es un reintento tardío, con todos los riesgos de un reintento.

Ejercicios

Ejercicio 1 — Lee el payload. Te llega este payload al Error Trigger de Cumbre. Di qué workflow falló, en qué nodo, con qué mensaje, y qué severidad le asignarías según la política de la lección 4:

{
  "execution": {
    "id": "1187",
    "url": "https://cumbre.n8n/workflow/refund/executions/1187",
    "error": { "message": "Payments API timeout after 3 retries", "stack": "…" },
    "lastNodeExecuted": "Emit refund",
    "mode": "trigger"
  },
  "workflow": { "id": "refund", "name": "issue-refund" }
}
Ver solución
  • Qué workflow falló: issue-refund (de workflow.name).
  • En qué nodo: Emit refund (de execution.lastNodeExecuted).
  • Con qué mensaje: "Payments API timeout after 3 retries" (de execution.error.message). Nota que el mensaje ya dice "after 3 retries": los reintentos de la lección 2 se agotaron, así que esto es terminal, no transitorio.
  • Severidad: critical. Es issue-refund, que en la política de la lección 4 es crítico siempre porque involucra dinero. Va a la rama de alerta a finanzas y a la cola de mensajes muertos.

Además, guardarías execution_id = "1187", que es lo que te va a permitir en la lección 6 cargar exactamente esta ejecución en el motor de replay para ver qué pasó.

Por qué funciona: el ejercicio te entrena a leer el payload real del Error Trigger y a traducirlo, campo por campo, a las columnas de la cola y a la decisión de la lección 4. Ese payload es la materia prima de todo el error workflow.

Ejercicio 2 — Diseña las columnas para un caso nuevo. Cumbre quiere poder responder, mirando la cola de mensajes muertos, la pregunta: "¿cuántos pedidos llevan más de 24 horas en pending sin reprocesar?". ¿Qué columnas de la tabla dead_letter necesitas para responderla, y qué consulta harías (en palabras, no necesariamente SQL exacto)?

Ver solución

Necesitas dos columnas que la tabla ya tiene: created_at (cuándo cayó el item en la cola) y status (si sigue pending).

La consulta, en palabras: cuenta los registros donde status = 'pending' y created_at es anterior a hace 24 horas. En SQL sería algo como filtrar por esas dos condiciones y contar.

Lo interesante es por qué esta pregunta importa: un item que lleva más de 24 horas en pending es una señal de que la revisión manual de la cola no está ocurriendo, o de que algo está sistemáticamente atascado. Es, en sí mismo, un candidato a alerta —una alerta de "la cola se está acumulando", que es distinta de la alerta de cada fallo individual—. Esto conecta con la idea de la lección 4 de alertar por tasas y no solo por eventos: no alertas por un item en la cola, pero sí por "hay diez items que llevan más de un día sin atender".

Por qué funciona: el ejercicio muestra que la cola de mensajes muertos no es solo un cajón donde tiras cosas; es una tabla que puedes consultar para entender la salud del sistema. Diseñar bien sus columnas —con marcas de tiempo y estado— es lo que la convierte de un basurero en una herramienta.

Ejercicio 3 — El reproceso peligroso. Un lote de cinco reembolsos cayó en la cola de mensajes muertos por una caída de dos horas de la API de pagos. La API ya volvió. Describe el flujo correcto para reprocesarlos sin arriesgar reembolsos dobles, nombrando las dos protecciones que lo hacen seguro y qué columna o dato aporta cada una.

Ver solución

El flujo correcto:

  1. Se leen los cinco items con status = 'pending' y source_workflow = 'issue-refund'.
  2. Para cada uno, se re-lanza la emisión del reembolso usando la misma clave de idempotencia que tenía el intento original —que está guardada dentro del payload completo, por eso se guardó entero—.
  3. Antes o durante, se consulta el ledger para ver si ese reembolso ya figura como emitido; si sí, se salta y se marca reprocessed sin volver a llamar a la API.
  4. Al terminar cada uno con éxito, se actualiza su status a reprocessed.

Las dos protecciones:

  • La clave de idempotencia (aportada por el payload guardado en la cola): protege del lado de la API. Si alguno de esos cinco reembolsos se había emitido antes de la caída —el mundo B: efecto hecho, confirmación perdida—, la API reconoce la clave repetida y no emite un segundo reembolso.
  • El estado (aportado por el ledger del Módulo 4 y la columna status de la cola): protege del lado del sistema. Evita reprocesar algo que ya se resolvió, y deja registro de qué se reprocesó.

El riesgo que se evita: sin la clave, reprocesar "para ponerse al día" emitiría un segundo reembolso de cualquiera de los cinco que ya había salido, porque un fallo terminal no garantiza que el efecto no ocurrió —solo que no se confirmó—.

Por qué funciona: reprocesar desde la cola de mensajes muertos es el reintento de la lección 2 llevado a su forma más tardía, hecho a mano horas después. Que se pueda hacer con seguridad depende enteramente de que las claves se derivaran de datos estables desde el Módulo 2. Todo el módulo se apoya en esa única decisión temprana.

Resumen y siguiente paso

En esta lección construiste las dos piezas que atrapan lo que las demás no pudieron resolver. Viste el nodo Error Trigger como el primer nodo de un error workflow central —cumbre-error-handler— que funciona como una centralita: un solo embudo al que llegan los fallos de los cuatro workflows, configurado en Options > Settings > Error workflow de cada uno, con los detalles de la documentación (un workflow con Error Trigger se maneja a sí mismo, no se prueba a mano, conviene asignarlo a todos). Desmenuzaste el payload real que recibe —workflow.name, execution.lastNodeExecuted, execution.error.message, execution.id y su enlace—, y lo convertiste, con un nodo Code de pura lógica, en la decisión de severidad de la lección 4. Y montaste la cola de mensajes muertos: la tabla dead_letter en el Postgres del ledger, con el payload completo y el status, para que ningún item se pierda en silencio. Cerraste con la regla de siempre: reprocesar desde la cola es un reintento tardío, y solo es seguro con la clave de idempotencia estable y el estado en el ledger.

Antes de avanzar deberías poder: configurar un error workflow y explicar por qué no se prueba ejecutándolo a mano; nombrar los campos del payload del Error Trigger que usarías para registrar y alertar; y explicar por qué la cola de mensajes muertos guarda el pedido completo y por qué reprocesarla necesita idempotencia.

Lo que no viste todavía es cómo se investiga un fallo cuando el mensaje del error no basta. Guardaste el execution.id de cada fallo en la cola —y ese identificador es una llave. La lección 6 la usa: con el motor de depuración de n8n 2.0 vas a cargar los datos de una ejecución real que ya pasó, volver a correrla paso a paso, y seguir la clave de idempotencia por cada nodo hasta ver exactamente dónde se creó un segundo reembolso. Es la herramienta que convierte el peor tipo de bug —el que aparece una de cada cien veces y desaparece cuando lo buscas— en uno reproducible y arreglable.

Recursos

  • Error Trigger — n8n Docs — la ficha oficial del nodo, con la estructura exacta del payload que recibe cuando falla un nodo y la variante para cuando falla el disparador.
  • Error handling — n8n Docs — cómo crear un error workflow, asignarlo en los settings del workflow, y las notas de que se maneja a sí mismo y no se prueba a mano.
  • Handle errors gracefully — n8n Docs — guía de diseño del manejo de errores de la que sale el patrón del error workflow central.
  • Postgres node — n8n Docs — el nodo con el que se crea y escribe la tabla dead_letter sobre el Postgres del Starter Kit.
  • Switch node — n8n Docs — el nodo que enruta el fallo por severidad hacia la alerta o la cola, dentro del error workflow.