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

6. Reproducir y trazar un bug de duplicado con el replay

Descripción

Al terminar esta lección vas a poder tomar el peor tipo de bug —"a veces Cumbre emite dos reembolsos, no sé cuándo ni por qué"— y convertirlo en uno reproducible y arreglable. Vas a usar el motor de depuración de n8n 2.0 para cargar los datos de una ejecución real que ya pasó dentro del editor, volver a correrla paso a paso, y trazar la clave de idempotencia por cada nodo hasta ver exactamente dónde se creó el segundo efecto. Vas a conocer la diferencia entre Retry with original workflow y Retry with currently saved workflow, cuándo usar cada una, y cómo seguir el valor de una variable dentro de un nodo Code sin adivinar. El resultado: un método para atrapar bugs intermitentes, que son los que más tiempo cuestan y más frustran.

Esto importa porque un bug que no puedes reproducir no lo puedes arreglar con confianza. Puedes creer que lo arreglaste, cambiar algo, y como el bug aparecía una de cada cien veces, pasar dos semanas sin verlo y concluir que quedó resuelto —hasta que reaparece—. El replay rompe ese ciclo: en vez de esperar a que el bug vuelva a ocurrir por azar, tomas la ejecución exacta en la que ocurrió, la reconstruyes en tu editor con sus datos reales, y la observas cuantas veces quieras. Un bug intermitente en producción se vuelve un bug determinista en tu pantalla. Esa es la diferencia entre depurar con método y depurar con suerte.

Conexión con el módulo: esta lección usa lo que la lección 5 guardó. Cuando un fallo cayó en la cola de mensajes muertos, guardaste su execution.id —ese identificador es la llave que abre el replay—. Y el bug que vas a cazar es, precisamente, el que todas las lecciones anteriores intentaron prevenir: un efecto duplicado, que aquí aparece cuando la protección de idempotencia (Módulo 2) tuvo una grieta. El replay es la herramienta de diagnóstico que cierra el módulo antes del capstone: reintentar (2) y compensar (3) reaccionan al fallo, alertar (4) y la cola (5) lo capturan, y el replay (6) te deja entenderlo. En el capstone vas a usar un replay como prueba de que un disparo duplicado no crea un segundo efecto.

El peor bug: el que aparece una de cada cien veces

Empecemos por entender por qué este tipo de bug es tan especialmente difícil, porque el método de la lección está diseñado contra esa dificultad.

La mayoría de los bugs son deterministas: haces X, pasa Y, siempre. Los arreglas porque los puedes reproducir a voluntad —repites X, ves Y, cambias algo, repites X, ya no ves Y—. Ese ciclo de "reproducir, cambiar, verificar" es la columna vertebral de la depuración.

Un bug intermitente rompe ese ciclo en su primer paso. "A veces salen dos reembolsos" significa que la mayoría de las veces sale uno. No puedes reproducirlo a voluntad: puedes procesar cincuenta pedidos y que todos salgan bien, no porque lo hayas arreglado, sino porque no se dio la combinación exacta que lo dispara. Y como no lo puedes reproducir, no puedes verificar un arreglo: cambias algo, procesas veinte pedidos que salen bien, y no sabes si fue tu cambio o fue suerte.

Los bugs intermitentes casi siempre nacen de una condición de carrera o de un estado que depende del tiempo: dos cosas que ocurren "casi al mismo tiempo" en un orden que normalmente es inofensivo pero que, en el instante justo, produce el problema. En Cumbre, el candidato número uno es la trampa de "verificar y luego actuar" del Módulo 2: el webhook dispara dos veces casi simultáneamente, las dos ejecuciones consultan el ledger antes de que ninguna haya escrito, las dos ven "este pedido no se ha procesado", y las dos siguen adelante y emiten un reembolso. Dos reembolsos. Y solo pasa cuando los dos disparos caen dentro de la ventana de milisegundos en que ninguno alcanzó a escribir —una de cada cien veces—.

La analogía es la caja negra de un avión. Un incidente que ocurre una vez entre miles de vuelos es imposible de reproducir pidiéndole al piloto "vuela otra vez a ver si pasa". Lo que hace posible investigarlo es que el avión grabó todo lo que ocurrió en ese vuelo exacto: cada dato, cada acción, en orden. Los investigadores no reproducen el vuelo; reproducen la grabación. El motor de replay de n8n es esa caja negra: cada ejecución quedó grabada con sus datos reales, y tú reproduces la grabación de la ejecución que falló, no una ejecución nueva con los dedos cruzados.

El motor de depuración de n8n 2.0

n8n guarda los datos de cada ejecución —qué entró a cada nodo, qué salió— y te deja cargarlos de vuelta en el editor. Vamos a ver las dos herramientas que importan.

Sobre las etiquetas y la disponibilidad. Esta guía se escribió con n8n 2.x en julio de 2026. Los nombres exactos de los botones y el detalle de qué se puede recargar han evolucionado entre versiones, y son de lo que conviene verificar en tu propio panel. Además, para que haya ejecuciones que recargar, tu instancia tiene que estar guardando las ejecuciones —incluidas las fallidas—: es una opción en los settings del workflow, y sin ella no hay grabación que reproducir. El concepto es estable; el texto del botón y la ubicación de la opción, verifícalos.

Debug in editor (Depurar en el editor). Es la herramienta central de la lección. Desde la lista de ejecuciones, tomas una ejecución pasada —por ejemplo, la que emitió el reembolso doble— y eliges depurarla en el editor. Según la documentación oficial, n8n copia los datos de esa ejecución dentro de tu workflow actual y los fija (pin) en el primer nodo. A partir de ahí, cuando ejecutas el workflow, no llega un evento nuevo del webhook: llegan los datos exactos de aquella ejecución, congelados. Reproduces la grabación.

Esto es lo que convierte un bug intermitente en determinista. La combinación exacta que disparó el duplicado —los datos precisos, en el estado preciso— ya no depende del azar de que el webhook dispare dos veces en la ventana justa: está fijada en el primer nodo, y la puedes correr una y otra vez, mirando cada paso.

Retry (reintentar la ejecución). Desde la lista de ejecuciones, además de depurar, puedes relanzar una ejecución fallida entera. Hay dos variantes, y la diferencia es importante:

  • Retry with original workflow (reintentar con el workflow original): re-ejecuta usando el workflow tal como estaba cuando ocurrió el fallo. Sirve para confirmar el comportamiento original sin que tus cambios recientes lo enturbien.
  • Retry with currently saved workflow (reintentar con el workflow guardado actual): re-ejecuta con la versión actual del workflow —la que quizás ya modificaste para arreglar el bug—, pero usando los datos de la ejecución vieja. Es como verificas un arreglo: mismos datos que fallaron, workflow corregido, ¿ahora sí sale bien?

La distinción práctica: usas Debug in editor para entender el bug —cargar los datos y trazar paso a paso—, y Retry with currently saved workflow para verificar el arreglo —los mismos datos que rompían, corriendo por tu versión corregida—.

Y la advertencia de siempre, que en el replay es fácil olvidar: reproducir una ejecución re-ejecuta sus efectos. Si recargas y corres la ejecución del reembolso doble sobre tu sistema conectado a la API real de pagos, podrías emitir más reembolsos de verdad. Para depurar un bug de duplicados nunca lo haces contra la API real; lo haces contra un entorno de prueba, o desconectando el efecto real, o —lo más simple para trazar— observando los datos sin dejar que el nodo del efecto llegue a llamar a nada. Vas a ver cómo en el ejemplo trabajado.

Trazar la clave de idempotencia paso a paso

El replay te pone los datos correctos delante; trazar es lo que haces con ellos. Trazar una variable es seguir su valor nodo por nodo para ver dónde deja de ser lo que esperabas. Para un bug de duplicado, la variable que trazas es la clave de idempotencia: si en algún nodo la clave cambió, o se calculó distinto, o la verificación contra el ledger dio un resultado inesperado, ahí está la grieta.

La restricción del nodo Code de n8n 2.0 acota tus herramientas, y está bien, porque las que quedan bastan:

  • console.log() escribe en la consola del navegador (no en el panel de salida, como viste en guías anteriores). Sirve para dejar rastros: console.log('key en este nodo:', key).
  • Devolver la variable en la salida es más cómodo para trazar: agregas temporalmente el valor que quieres inspeccionar al objeto que el nodo devuelve, y lo lees en el panel OUTPUT sin cambiar de ventana.
  • Los paneles INPUT y OUTPUT de cada nodo te muestran, con los datos fijados del replay, exactamente qué entró y qué salió de ese nodo en la ejecución que estás reproduciendo.

Recuerda la restricción: dentro de un nodo Code de n8n 2.0 no hay HTTP ni acceso al sistema de archivos; en Cloud, solo crypto y moment. Para trazar no necesitas nada de eso —solo leer valores y devolverlos—, así que la restricción no te estorba.

Ejemplo trabajado: cazar el reembolso doble de Cumbre

Vamos a reproducir y trazar el bug completo. El síntoma reportado: "el pedido ORD-3180 de Luna Coffee recibió dos reembolsos el martes". En la cola de mensajes muertos (lección 5) tienes guardado el execution.id de la ejecución sospechosa. Empecemos.

Fase 1 — Cargar la grabación.

Dónde estás: tienes el execution.id del incidente, sacado de la tabla dead_letter o de la lista de ejecuciones.

Paso 1.1. Abre el workflow issue-refund y ve a su lista de ejecuciones. Encuentra la ejecución del incidente por su id.

Paso 1.2. Elige depurarla en el editor (verifica la etiqueta exacta del botón en tu versión).

Qué esperar. n8n copia los datos de esa ejecución en tu editor y los fija en el primer nodo. Vas a ver el primer nodo con un indicador de que sus datos están "pineados" —congelados—. A partir de ahora, ejecutar el workflow usa esos datos, no un evento nuevo. Ya tienes la grabación cargada.

Fase 2 — Desconectar el efecto real antes de tocar nada.

Dónde estás: con la grabación cargada, pero el workflow todavía apunta a la API real de pagos. Si lo corres así, emites reembolsos de verdad.

Paso 2.1. Antes de ejecutar nada, desactiva o reemplaza el nodo HTTP Request que llama a la API de pagos —el que emite el reembolso—. Lo más limpio para trazar es sustituirlo temporalmente por un nodo que solo devuelva una respuesta simulada, para que el flujo siga pero sin llamar al mundo real.

Qué esperar. Ahora puedes correr el replay cuantas veces quieras sin riesgo de emitir un solo reembolso real. Este paso no es opcional: reproducir un bug de duplicados contra la API real crea duplicados reales.

Fase 3 — Trazar la clave de idempotencia.

Dónde estás: grabación cargada, efecto real desconectado, listo para observar.

Paso 3.1. En el nodo Code que calcula la clave de idempotencia, agrega temporalmente el valor a la salida para verlo en el panel:

// ============================================================
// Nodo: Code — "Build refund key" (con trazado temporal)
// Modo: Run Once for Each Item
//
// Se agrega _trace_key a la salida SOLO para depurar; se quita después.
// ============================================================

const item = $json;

// La clave debería derivarse de datos estables del pedido.
const refundKey = `refund:${item.order_id}`;

return {
  json: {
    ...item,
    idempotency_key: refundKey,
    _trace_key: refundKey,          // ← temporal: para leerlo en el panel OUTPUT
  },
};

Paso 3.2. Ejecuta el paso y lee el panel OUTPUT. Anota el valor de _trace_key.

Paso 3.3. Ve al nodo que consulta el ledger para verificar si este pedido ya tiene un reembolso —la verificación de dedup—. Mira su panel INPUT (qué clave le llegó) y su OUTPUT (qué respondió el ledger).

Qué esperar, y aquí está el bug. Con los datos reales del incidente cargados, vas a ver que en esta ejecución el nodo de verificación del ledger respondió "no existe reembolso previo" —y por eso el flujo siguió y emitió el reembolso—. Hasta ahí, correcto. El problema no está dentro de esta ejecución: está en que hubo dos ejecuciones casi simultáneas que ambas leyeron el ledger antes de que ninguna escribiera. Es la trampa de "verificar y luego actuar" del Módulo 2. Al trazar la clave, confirmas que la clave estaba bien calculadarefund:ORD-3180 en las dos ejecuciones, idéntica— y que el fallo no fue de la clave sino del momento de la verificación: las dos preguntaron "¿ya existe?" antes de que ninguna respondiera "sí, yo".

Fase 4 — Confirmar la hipótesis con la ejecución hermana.

Dónde estás: sospechas una condición de carrera entre dos ejecuciones.

Paso 4.1. En la lista de ejecuciones, busca la otra ejecución de ORD-3180 del martes —la hermana del duplicado—. Compara sus tiempos: si las dos arrancaron con una diferencia de milisegundos, la hipótesis de la carrera se confirma.

Paso 4.2. Carga también esa ejecución y traza su clave: vas a encontrar la misma refund:ORD-3180, y el mismo "no existe reembolso previo" en su verificación del ledger. Dos ejecuciones, misma clave, ambas viendo el ledger vacío, ambas emitiendo. El bug queda reproducido y entendido.

Fase 5 — Verificar el arreglo.

Dónde estás: entiendes el bug —"verificar y luego actuar" sin protección atómica— y sabes el arreglo del Módulo 2: la clave de idempotencia tiene que hacerse valer con una escritura condicional atómica en el ledger (un INSERT con restricción de unicidad que falla si la clave ya existe), no con un "leer y luego decidir". Así, de dos ejecuciones simultáneas, solo una logra insertar la clave; la otra choca contra la restricción y se detiene sin emitir.

Paso 5.1. Aplica el arreglo en el workflow (el INSERT ... ON CONFLICT o equivalente del Módulo 4), guárdalo.

Paso 5.2. Desde la lista de ejecuciones, usa Retry with currently saved workflow sobre la ejecución del incidente: mismos datos que fallaron, corriendo por tu versión corregida.

Qué esperar. Con el arreglo, la ejecución que antes emitía un reembolso ahora, al intentar insertar la clave que la hermana ya insertó, choca contra la restricción de unicidad y se detiene antes del efecto. El duplicado no se produce. Acabas de verificar el arreglo contra los datos exactos que lo rompían, no contra pedidos nuevos con los dedos cruzados. Eso es depurar con método.

Fase 6 — Limpiar.

Paso 6.1. Quita el _trace_key temporal del nodo Code. Reconecta el nodo real del efecto que desconectaste en la fase 2. Publica.

Los límites del replay: lo que no puede reproducir

Por honestidad, conviene saber qué no te da el replay, porque tratarlo como omnisciente lleva a conclusiones falsas.

El replay reproduce los datos de una ejecución, pero no reproduce el timing exacto entre ejecuciones distintas. Aquí está la sutileza del bug de la condición de carrera: el duplicado nació porque dos ejecuciones corrieron casi al mismo tiempo y se pisaron. Cuando cargas una de esas ejecuciones con Debug in editor, la reproduces sola, en tu editor, sin su hermana corriendo en paralelo. Es decir: el replay te deja ver perfectamente qué hizo cada ejecución por separado —y con eso confirmas que las dos calcularon la misma clave y las dos vieron el ledger vacío—, pero no recrea el instante exacto en que las dos se solaparon. La carrera la deduces comparando las dos grabaciones y sus tiempos de arranque, no la observas en vivo.

Esto no es una falla; es la naturaleza de las condiciones de carrera. El valor del replay aquí no es reproducir la colisión, sino darte los datos precisos de cada participante para entender que colisionaron. Con eso alcanza para diagnosticar y arreglar, porque el arreglo —la escritura condicional atómica— no depende de recrear el timing, sino de hacer que el timing deje de importar: con una restricción de unicidad, no importa cuán cerca corran las dos ejecuciones, solo una gana.

El segundo límite: el replay usa los datos grabados, pero el estado externo pudo cambiar. Si la ejecución original consultó el ledger y este ha cambiado desde entonces —porque otros pedidos se procesaron—, un Retry with currently saved workflow que vuelva a consultar el ledger verá el estado actual, no el de aquel martes. Por eso, para trazar la lógica de una ejecución vieja, Debug in editor con los datos fijados es más fiel que un Retry que vuelve a tocar el mundo. Ten presente qué partes de tu replay usan datos congelados y qué partes vuelven a consultar algo que pudo moverse.

Por qué el replay cierra el módulo

Vale la pena ver cómo esta lección amarra todo lo anterior, porque no es casual que vaya casi al final.

Cada pieza del módulo dejó un rastro que el replay aprovecha. Los reintentos de la lección 2 quedan registrados en el historial de ejecuciones —execution.retryOf te dice qué fue reintento de qué—. Las compensaciones de la lección 3 dejan su huella en el ledger. La política de alertas de la lección 4 es la que te avisó de que había un duplicado que investigar. Y la cola de mensajes muertos de la lección 5 te guardó el execution.id con el que abriste el replay. El replay no es una herramienta aislada: es la que lee todo lo que las demás piezas escribieron, para reconstruir la historia de un fallo.

Y hay una simetría bonita con la que abrimos el módulo. La primera lección prometía que al terminar sabrías "reproducir un bug de duplicado con el motor de replay". Acabas de hacerlo, y la razón por la que pudiste es que todo el sistema estuvo diseñado, desde el Módulo 2, para dejar rastros estables: claves derivadas de datos que no cambian, un ledger que registra la verdad, una cola que no pierde el contexto. Un sistema que no deja rastros no se puede depurar. El replay es la recompensa de haber diseñado con disciplina desde el principio.

Errores comunes

Reproducir el bug contra la API real y crear duplicados de verdad (práctico). Qué pasa: se carga la ejecución del reembolso doble en el editor y se ejecuta para investigar, con el workflow todavía apuntando a la API real de pagos. El replay emite reembolsos reales —y como estás corriéndolo varias veces para trazar, emites varios—. Acabas de convertir un incidente en tres. Por qué pasa: en el modo de depuración se siente que estás "solo mirando", y es fácil olvidar que ejecutar re-ejecuta los efectos. Cómo detectarlo: antes de ejecutar un replay, pregúntate "¿este workflow, si corre, va a tocar el mundo real?". Si toca dinero, correos o cualquier efecto externo, la respuesta tiene que ser no. Cómo corregirlo: desconecta o simula el nodo del efecto real antes de la primera ejecución del replay, como en la fase 2 del ejemplo. Reproducir un bug de duplicados sin desconectar el efecto es la receta para multiplicarlo.

Verificar el arreglo con pedidos nuevos en vez de con la ejecución que falló (conceptual). Qué pasa: se cree entender el bug, se cambia algo, y se verifica procesando pedidos nuevos —que salen bien—. Como el bug era intermitente, salir bien no prueba nada: quizás no se dio la condición que lo dispara. Se declara resuelto, y semanas después reaparece. Por qué pasa: procesar pedidos nuevos es lo más natural y el bug parece irse. Cómo detectarlo: si tu verificación no usa exactamente los datos que produjeron el fallo, no estás verificando el arreglo del bug; estás verificando que el sistema funciona en el caso fácil. Cómo corregirlo: usa Retry with currently saved workflow sobre la ejecución del incidente. Los mismos datos que rompían, corriendo por tu versión corregida, es la única prueba que vale para un bug intermitente.

Trazar sin fijar los datos, corriendo eventos nuevos cada vez (práctico). Qué pasa: en vez de cargar la ejecución con Debug in editor, se dispara el webhook a mano una y otra vez esperando que el bug aparezca. Como es intermitente, casi nunca aparece, y cuando aparece, los datos ya no son los mismos que la próxima vez, así que no puedes comparar. Por qué pasa: disparar el trigger es el reflejo de siempre, y la idea de "cargar una ejecución vieja" no es obvia. Cómo detectarlo: si estás esperando a que el bug "vuelva a salir" en vez de reproducir uno que ya salió, estás depurando con suerte. Cómo corregirlo: carga la ejecución concreta del incidente con Debug in editor, que fija los datos exactos en el primer nodo. Con los datos fijados, el bug deja de depender del azar y puedes correrlo y trazarlo cuantas veces necesites.

Ejercicios

Ejercicio 1 — Ordena el método. Estos seis pasos están desordenados. Ponlos en el orden correcto para reproducir y arreglar el bug del reembolso doble sin causar daño:

(a) Aplicar el arreglo (escritura condicional atómica en el ledger) y guardar. (b) Cargar la ejecución del incidente con Debug in editor. (c) Retry with currently saved workflow sobre la ejecución del incidente. (d) Desconectar o simular el nodo del efecto real. (e) Trazar la clave de idempotencia y confirmar la condición de carrera. (f) Quitar el trazado temporal y reconectar el efecto real.

Ver solución

El orden correcto: (b) → (d) → (e) → (a) → (c) → (f).

  1. (b) Cargar la ejecución con Debug in editor: fijas los datos exactos del incidente en el primer nodo.
  2. (d) Desconectar el efecto real: antes de ejecutar nada, para no emitir reembolsos de verdad al reproducir.
  3. (e) Trazar la clave: sigues el valor paso a paso y confirmas que la clave estaba bien pero dos ejecuciones simultáneas leyeron el ledger vacío —la condición de carrera—.
  4. (a) Aplicar el arreglo: la escritura condicional atómica que hace que solo una de dos ejecuciones logre insertar la clave.
  5. (c) Verificar con Retry with currently saved workflow: los mismos datos que fallaban, por la versión corregida; confirmas que el duplicado ya no se produce.
  6. (f) Limpiar: quitas el trazado temporal y reconectas el efecto real antes de publicar.

El punto crítico del orden es que (d) va antes de cualquier ejecución, y (c) va después de (a) —verificar antes de arreglar no tendría sentido—.

Por qué funciona: el método tiene una lógica de seguridad y una de diagnóstico entrelazadas. Primero te proteges (b, d), después entiendes (e), después arreglas (a), después verificas contra los datos reales (c), y al final limpias (f). Saltarte el orden —arreglar antes de entender, o ejecutar antes de desconectar— es donde la depuración se tuerce.

Ejercicio 2 — Debug in editor o Retry. Para cada situación, di si usarías Debug in editor, Retry with original workflow, o Retry with currently saved workflow, y por qué:

(a) Quieres entender por qué falló una ejecución, trazando sus datos paso a paso. (b) Ya arreglaste el workflow y quieres confirmar que los datos que fallaban ahora pasan bien. (c) Quieres confirmar que reproduces el comportamiento original antes de tocar nada.

Ver solución

(a) Debug in editor. Carga los datos de la ejecución en el editor y los fija en el primer nodo, que es lo que te deja trazar nodo por nodo. Es la herramienta de entender.

(b) Retry with currently saved workflow. Corre los datos de la ejecución vieja por tu workflow actual —el que ya arreglaste—. Es la única forma de verificar un arreglo contra los datos exactos que lo rompían. Es la herramienta de verificar el arreglo.

(c) Retry with original workflow. Re-ejecuta con el workflow tal como estaba, sin tus cambios, para confirmar el comportamiento original limpio. Es la herramienta de confirmar la línea base.

La distinción de fondo: Debug in editor es para trazar dentro del editor con los datos fijados; las dos variantes de Retry relanzan la ejecución completa, diferenciándose en qué versión del workflow usan —la vieja (original) o la tuya actual (currently saved)—.

Por qué funciona: elegir la herramienta correcta según lo que quieres lograr —entender, verificar el arreglo, o confirmar la base— es lo que hace la depuración eficiente. Usar Retry cuando querías trazar, o Debug cuando querías verificar, te hace dar vueltas.

Ejercicio 3 — Traza tú. Te dan este nodo Code que calcula la clave de idempotencia de un reembolso, y te dicen que "a veces genera claves distintas para el mismo pedido, y por eso a veces se cuela un duplicado". Encuentra el bug trazando mentalmente la clave, y di cómo lo confirmarías con el replay.

// Nodo: Code — "Build refund key" (con un bug)
const item = $json;
const refundKey = `refund:${item.order_id}:${Date.now()}`;
return { json: { ...item, idempotency_key: refundKey } };
Ver solución

El bug está en Date.now(). La clave se construye con refund:${item.order_id}:${Date.now()}, y Date.now() devuelve el instante actual en milisegundos —un valor que cambia en cada ejecución—. Eso significa que dos ejecuciones del mismo pedido ORD-3180, al correr en momentos distintos, generan claves distintas: refund:ORD-3180:1719...801 y refund:ORD-3180:1719...machine847. Como las claves son distintas, la API de pagos las ve como dos reembolsos diferentes y emite los dos. La protección de idempotencia se cae porque la clave no es estable.

Es exactamente el error contra el que advertía el Módulo 2 y que repasamos en la lección 5: la clave debe derivarse de datos que no cambian entre reintentos ni entre ejecuciones —el order_id, el tipo de operación—, nunca de la hora ni de un valor aleatorio. El arreglo es quitar Date.now(): const refundKey = `refund:${item.order_id}`;.

Cómo lo confirmarías con el replay: cargas dos ejecuciones distintas del mismo ORD-3180 con Debug in editor, y trazas la clave en cada una agregándola temporalmente a la salida. Vas a ver que el order_id es idéntico en las dos pero la clave completa difiere en la parte del timestamp. Eso confirma que el fallo no es del pedido ni del ledger, sino de que la clave incorpora un valor que cambia. Después aplicas el arreglo y verificas con Retry with currently saved workflow que ahora las dos ejecuciones producen la misma clave.

Por qué funciona: este es el bug de idempotencia más común que existe, y es sutil porque el código "parece" correcto —hasta genera una clave con el order_id dentro—. Trazar la clave y verla diferir entre dos ejecuciones del mismo pedido es lo que lo hace evidente. Sin el replay, seguirías adivinando por qué "a veces" se duplica.

Resumen y siguiente paso

En esta lección tomaste el peor tipo de bug —el intermitente, que aparece una de cada cien veces y desaparece cuando lo buscas— y aprendiste a convertirlo en determinista. Viste por qué estos bugs rompen el ciclo normal de depuración: no se pueden reproducir a voluntad, casi siempre nacen de una condición de carrera o de un estado que depende del tiempo, y por eso necesitas la caja negra. Conociste el motor de depuración de n8n 2.0: Debug in editor, que copia los datos de una ejecución real y los fija en el primer nodo para que reproduzcas la grabación en vez de esperar a que el bug vuelva; y las dos variantes de Retry —con el workflow original para confirmar la base, con el guardado actual para verificar un arreglo—. Trazaste la clave de idempotencia del reembolso doble de Cumbre paso a paso, con la disciplina de desconectar el efecto real antes de tocar nada, y descubriste que el fallo era la trampa de "verificar y luego actuar" del Módulo 2 —dos ejecuciones simultáneas leyendo el ledger vacío—. Y viste que el replay cierra el módulo porque lee los rastros que todas las demás piezas dejaron.

Antes de avanzar deberías poder: cargar una ejecución pasada con Debug in editor y explicar qué hace con los datos; elegir entre Debug, Retry original y Retry currently saved según quieras entender, confirmar la base o verificar un arreglo; y trazar una variable dentro de un nodo Code para encontrar dónde deja de ser lo que esperabas —incluyendo el clásico Date.now() en una clave de idempotencia—.

Ya tienes las cinco piezas técnicas del módulo: reintentos, compensaciones, alertas, error workflows con cola de mensajes muertos, y el replay. Lo que falta antes del capstone es una conversación honesta sobre los límites. Todo lo que construiste corre en una instancia Community self-hosted a costo cero —y eso es genuinamente notable—, pero hay una frontera donde la correctitud que diseñaste se topa con necesidades de operación que Community no cubre: guardar los secretos del ledger en un gestor externo, probar los contratos en entornos separados, versionar los workflows con Git para un equipo. La lección 7 traza esa línea con precisión: qué resuelve Community a $0, qué exige pagar, y por qué el resto vive en n8n-production-maintenance-guide y no aquí.

Recursos

  • Debug and re-run past executions — n8n Docs — la página oficial de Debug in editor: cómo carga los datos de una ejecución pasada y los fija en el primer nodo, base de todo el método de esta lección.
  • Executions — n8n Docs — la lista de ejecuciones desde donde cargas una ejecución al editor y lanzas Retry con el workflow original o el guardado actual.
  • Error handling — n8n Docs — incluye el reintento de ejecuciones y cómo retryOf relaciona un reintento con su ejecución original en el historial.
  • Code node — n8n Docs — el nodo donde trazas la clave de idempotencia, con sus límites en 2.0 (sin HTTP ni sistema de archivos; en Cloud solo crypto y moment).
  • Workflow settings — n8n Docs — dónde activas que se guarden las ejecuciones (incluidas las fallidas), sin lo cual no hay grabación que reproducir.