200 OK, pero el cliente no recibe nada: tres bugs de un agente n8n en producción

Exactamente 20 días después de cerrar el primer bug, apareció un segundo. Esta vez la culpable no era la LLM, sino las capas que la rodean: caracteres invisibles, validaciones y casillas desactivadas en la configuración. Tres bugs de naturaleza distinta: - **Etiquetas de protocolo en lugar de respuesta**: el modelo GLM responde solo con `[VERDICT:…] [TASK:NONE]`, el validador lo deja pasar como contenido válido, Format Response elimina las etiquetas y al cliente le llega `\n\n \n` - **El límite de Meta de 1000 bytes**: un formulario en ruso (nombre, fecha, sector, teléfono) ocupa 1030 bytes —una letra son 2 bytes, un emoji 3–4— y la API falla con un Bad Request - **Catálogo en lugar de selección**: no se definió el Schema Type, n8n dedujo el esquema a partir de un esquema en vez de un ejemplo, el campo category terminó al fondo, y la herramienta devuelve todo el surtido

Procesado por IA desde Habr AI; editado por Hamidun News
200 OK, pero el cliente no recibe nada: tres bugs de un agente n8n en producción
Fuente: Habr AI. Collage: Hamidun News.
Escuchar artículo

Esta es la segunda historia sobre un agente de IA en producción. Hace 20 días cerré el bug del Reasoning Lock: el modelo gastaba tokens en pensamientos ocultos y enviaba una cadena vacía al cliente, aunque el estado marcara éxito. Añadí un validador de respuestas vacías y telemetría. Escribí: «el log está vacío, las capas funcionan». El log se mantuvo en silencio 20 días. Luego los gestores enviaron nuevas quejas a través del cliente: el usuario escribe por Direct y el agente se queda callado. Pero esta vez la culpable no era la LLM.

Arquitectura del proyecto: El agente de n8n funciona en producción para un taller de joyería. Por las noches recibe solicitudes en Direct de [REDACTADO]gram, mantiene la conversación, recopila un formulario (nombre, fecha de nacimiento, sector, teléfono) y crea una oportunidad en amoCRM. Stack: n8n (self-hosted) + Redis (memoria de conversaciones, TTL de 30 días) + LangChain. La LLM cambió: en lugar de DeepSeek V4 Pro, ahora se usa z-ai/glm-5.3-flash a través de OpenRouter, con qwen/qwen3.8-flash como respaldo. El workflow pasó de 64 a 88 nodos, y se implementó el manejo de comentarios y la lógica de reintentos.

Bug 1:

Etiquetas de protocolo en lugar de una respuesta humana

Abrí las ejecuciones: todo limpio. Ni una sola ejecución fallida. La hipótesis de «se repitió el Reasoning Lock» queda descartada: el modelo responde, y rápido. El único rastro en amoCRM: la clienta respondió «Sí» a las 20:06 a una confirmación de pedido (enviada a las 20:02), y después solo un registro en MESSENGER-ID; en Direct, silencio total.

Abrí la ficha de OpenRouter: 871 tokens de completion, de los cuales 766 eran de reasoning, dejando 105 tokens visibles. En volumen, eso son etiquetas de servicio del protocolo: `[VERDICT:ORDER:DATA|HOT] [NEED_MANAGER_CALL] [STAGE:PERSONAL]`

El modelo GLM sí respondió. Según el protocolo. Ni una sola palabra humana. DeepSeek 4.0 PRO se replegaba sobre sí mismo; GLM resultó ser el alumno modelo: no se requiere texto humano, basta con entregar el formulario.

El validador no lo detectó porque solo comprueba la vacuidad (el output no está vacío: pasa). El invariante «contenido vacío + tokens gastados + finish_reason stop ≠ éxito» también falló: se gastaron tokens (sí), finish_reason era stop (sí), el contenido no estaba vacío (sí). Comprobaba la ausencia de vacío, no la presencia de texto humano.

Después, el nodo Format Response elimina las etiquetas por diseño, dejando `\n\n \n` (una cadena no vacía para n8n, caracteres para Meta, nada para el cliente). La comprobación previa al envío verifica si el nodo se ejecutó, no su contenido.

Bug 2:

El límite de Meta de 1000 bytes rompe el texto en ruso

Tras el diagnóstico, apareció un nodo rojo en el paso final de envío:

``` NodeApiError: Bad request - please check your parameters Send direct message1 ```

El fallo ocurría en el formulario: nombre, fecha, sector, teléfono, párrafos separados con `\n\n`. El mensaje más largo en la vida del agente. La documentación de la API de Meta dice: Message text must be UTF-8 and be 1000 bytes or less. No son caracteres, son bytes.

En UTF-8, una letra rusa ocupa 2 bytes, un emoji 3–4 bytes (✨ = 3, 💎 = 4). 1000 bytes son aproximadamente 500–550 caracteres de texto en ruso. Probé con un texto en ruso de 570 caracteres: ocupó 1030 bytes. Un formulario con nombre, fecha, sector, tres párrafos y un «¿Todo correcto?» supera fácilmente ese límite. Lo más frustrante: `\n\n` no es un bug, son párrafos normales, como en Word; un pipeline optimizado para el reasoning tropezó con un simple salto de línea.

Bug 3:

Catálogo en lugar de selección: Schema Type incorrecto

Mientras resolvía el problema del envío, llegó otra queja de los gestores: «el agente muestra lo que no es». Piden pendientes y suelta medio catálogo; piden relojes, y de nuevo medio catálogo.

Del catálogo se encarga el nodo-herramienta get_product_catalog1. Specify Input Schema estaba activado, pero no se había definido el Schema Type. n8n usa el valor por defecto: «Generate From JSON Example» (dame un ejemplo y deduzco el esquema). Pero en jsonSchemaExample no había un ejemplo de datos, sino el propio JSON Schema, con campos como type, properties, required, category (con enum: bracelets, earrings, necklaces, luch, candles) y description.

n8n no valida el contenido: tomó honestamente el esquema por un ejemplo y dedujo un esquema a partir de otro esquema. Las claves superiores type, properties, required se convirtieron en el ejemplo del objeto, y category quedó relegada al fondo. La herramienta lee inputData.json.category, obtiene undefined, y cae al valor por defecto all. Cualquier solicitud devuelve todo el surtido en lugar de una selección. Un clic para arreglarlo (Schema Type → «Define using JSON Schema»), una hora y media para encontrarlo.

Conclusión

Tres naturalezas distintas de un mismo problema: el modelo responde con etiquetas de protocolo en lugar de texto, la API tiene un límite rígido en bytes en lugar de caracteres, y la configuración de la herramienta se esconde tras un valor por defecto incorrecto. Un nodo verde es el peor tipo de nodo rojo: al menos con un fallo sabes dónde buscar.

Historia

Preguntas frecuentes sobre la noticia

¿Qué se sabe sobre Bug 1: Etiquetas de protocolo en lugar de una respuesta humana?

Abrí las ejecuciones: todo limpio. Ni una sola ejecución fallida. La hipótesis de «se repitió el Reasoning Lock» queda descartada: el modelo responde, y rápido. El único rastro en amoCRM: la clienta respondió «Sí» a las 20:06 a una confirmación de pedido (enviada a las 20:02), y después solo un registro en MESSENGER-ID; en Direct, silencio total. Abrí la ficha de OpenRouter: 871 tokens de completion, de los cuales 766 eran de reasoning, dejando 105 tokens visibles. En volumen, eso son etiquetas de servicio del protocolo: `[VERDICT:ORDER:DATA|HOT] [NEED_MANAGER_CALL] [STAGE:

¿Qué se sabe sobre Bug 2: El límite de Meta de 1000 bytes rompe el texto en ruso?

Tras el diagnóstico, apareció un nodo rojo en el paso final de envío: ``` NodeApiError: Bad request - please check your parameters Send direct message1 ``` El fallo ocurría en el formulario: nombre, fecha, sector, teléfono, párrafos separados con `\n\n`. El mensaje más largo en la vida del agente. La documentación de la API de Meta dice: Message text must be UTF-8 and be 1000 bytes or less. No son caracteres, son bytes. En UTF-8, una letra rusa ocupa 2 bytes, un emoji 3–4 bytes (✨ = 3, 💎 = 4). 1000 bytes son aproximadamente 500–550 caracteres de texto en ruso. Probé con un

¿Qué se sabe sobre Bug 3: Catálogo en lugar de selección: Schema Type incorrecto?

Mientras resolvía el problema del envío, llegó otra queja de los gestores: «el agente muestra lo que no es». Piden pendientes y suelta medio catálogo; piden relojes, y de nuevo medio catálogo. Del catálogo se encarga el nodo-herramienta get_product_catalog1. Specify Input Schema estaba activado, pero no se había definido el Schema Type. n8n usa el valor por defecto: «Generate From JSON Example» (dame un ejemplo y deduzco el esquema). Pero en jsonSchemaExample no había un ejemplo de datos, sino el propio JSON Schema, con campos como type, properties, required, category (con

ZK
Zhemal Khamidun — CPO de Alpina Digital, product manager de AlpinaGPT. Crea productos de IA desde 2019. Hamidun News es un proyecto personal que creció hasta ser un agregador en producción con más de 6.000 lectores.
✦ Hands-On AI Engineering · Hamidun Academy

Build & Ship Production AI Agents Without Boilerplate Code

Start your free 72-hour trial at Hamidun Academy (academy.hamidun.com) — interactive lessons on Claude Code & Cursor, live Tech Radar, and production agent templates.

Start 72h Free Trial →Vibe-Coding Intensive
¿Qué te parece?
Cargando comentarios…