200 OK, mais le client ne reçoit rien : trois bugs d'un agent n8n en production
Exactement 20 jours après la clôture du premier bug, un second est apparu. Cette fois, ce n'est pas le LLM qui est fautif, mais les couches autour : caractères invisibles, contrôles de validation et cases désactivées dans la configuration. Trois bugs de natures différentes : • **Balises internes au lieu d'une réponse** : le modèle GLM ne répond qu'avec `[VERDICT:…] [TASK:NONE]`, le validateur laisse passer ce contenu comme valide, le nœud Format Response supprime les balises, et il ne reste au client que `\n\n \n` • **Limite de 1000 octets chez Meta** : un formulaire en russe (nom, date, domaine, téléphone) pèse 1030 octets — une lettre russe fait 2 octets, un emoji 3 à 4, et l'API renvoie une erreur Bad request • **Catalogue au lieu d'une sélection** : le Schema Type n'est pas défini, n8n a déduit le schéma à partir du schéma lui-même au lieu d'un exemple, le champ category s'est retrouvé tout en bas, et l'outil renvoie tout l'assortiment
Traité par IA depuis Habr AI ; édité par Hamidun News
Voici la deuxième histoire d'un agent IA en production. Il y a 20 jours, j'avais clos le bug Reasoning Lock : le modèle dépensait ses tokens en pensées cachées et envoyait une chaîne vide au client, alors que le statut indiquait un succès. J'avais ajouté un validateur de réponse vide ainsi que de la télémétrie. J'avais écrit : « le log est vide — les couches fonctionnent ». Le log est resté silencieux pendant 20 jours. Puis les managers ont transmis de nouvelles plaintes via le client : le client écrit en message privé, l'agent reste muet. Mais cette fois, ce n'est pas le LLM qui est en cause.
Architecture du projet : L'agent n8n fonctionne en production pour un atelier de bijouterie. La nuit, il reçoit les demandes en message direct sur un réseau social, mène la conversation, collecte un formulaire (nom, date de naissance, domaine, téléphone) et crée une opportunité dans amoCRM. Stack : n8n (self-hosted) + Redis (mémoire des conversations, TTL de 30 jours) + LangChain. Le LLM a changé : à la place de DeepSeek V4 Pro, c'est désormais z-ai/glm-5.3-flash via OpenRouter, avec qwen/qwen3.8-flash en secours. Le workflow est passé de 64 à 88 nœuds, avec l'ajout de la gestion des commentaires et d'une logique de retry.
Bug 1 : des balises de protocole internes au lieu d'une réponse humaine
J'ai ouvert les executions — tout est propre. Aucune exécution n'a échoué. L'hypothèse « le Reasoning Lock s'est reproduit » tombe à l'eau : le modèle répond, et vite. La seule trace dans amoCRM : la cliente a répondu « Oui » à 20h06 pour confirmer la commande (envoyée à 20h02), puis seulement une entrée dans MESSENGER-ID — silence côté message direct.
J'ai ouvert la fiche OpenRouter : 871 tokens de completion, dont 766 de reasoning — il ne reste que 105 tokens visibles. En volume, ce sont des balises de protocole internes : `[VERDICT:ORDER:DATA|HOT] [NEED_MANAGER_CALL] [STAGE:PERSONAL]`
Le modèle GLM a bien répondu. Selon le protocole. Pas un seul mot humain. DeepSeek 4.0 PRO se repliait sur lui-même ; GLM s'est révélé être un élève trop modèle : pas de texte humain prévu — il rend juste le formulaire.
Le validateur n'a rien détecté car il ne vérifie que l'absence de vide (output non vide = pass). L'invariant « content vide + tokens dépensés + finish_reason stop ≠ succès » a lui aussi échoué : les tokens sont dépensés (oui), finish_reason est stop (oui), le content n'est pas vide (oui). Il vérifiait l'absence de vide, pas la présence de texte humain.
Ensuite, le nœud Format Response supprime les balises par conception — il ne reste que `\n\n \n` (une chaîne non vide pour n8n, des caractères pour Meta, rien pour le client). Le garde-fou avant l'envoi vérifie si le nœud isExecuted, pas son contenu.
Bug 2 : la limite de 1000 octets de
Meta fragmente le texte russe
Après ce diagnostic, un nœud rouge est apparu sur l'envoi final :
``` NodeApiError: Bad request - please check your parameters Send direct message1 ```
L'échec survenait sur le formulaire : nom, date, domaine, téléphone, paragraphes séparés par `\n\n`. Le message le plus long jamais produit par l'agent. La documentation de l'API Meta précise : Message text must be UTF-8 and be 1000 bytes or less. Pas des caractères, des octets.
En UTF-8, une lettre russe occupe 2 octets, un emoji 3 à 4 octets (✨ = 3, 💎 = 4). 1000 octets représentent environ 500 à 550 caractères de texte russe. J'ai testé un texte russe de 570 caractères : il pesait 1030 octets. Un formulaire avec nom, date, domaine, trois paragraphes et un « C'est bien ça ? » dépasse facilement la limite. Le plus frustrant : `\n\n` n'est pas un bug, ce sont de simples paragraphes humains, comme dans Word — un pipeline conçu pour le reasoning a trébuché sur un simple retour à la ligne.
Bug 3 : un catalogue entier au lieu d'une sélection — mauvais
Schema Type
Alors que je réglais le problème d'envoi, une autre plainte des managers est arrivée : « l'agent montre n'importe quoi ». On demande des boucles d'oreilles, il balance la moitié du catalogue ; on demande des montres, même résultat.
Le catalogue est géré par le nœud outil get_product_catalog1. L'option Specify Input Schema est activée, mais le Schema Type n'est pas défini. n8n prend la valeur par défaut — « Generate From JSON Example » : donne-moi un exemple, j'en déduis le schéma. Or dans jsonSchemaExample, il n'y avait pas un exemple de données mais le JSON Schema lui-même, avec les champs type, properties, required, category (avec l'enum : bracelets, earrings, necklaces, luch, candles) et description.
n8n ne vérifie pas le contenu — il a honnêtement pris le schéma pour un exemple et en a déduit un schéma à partir d'un schéma. Les clés de premier niveau type, properties, required sont devenues l'exemple d'objet, et category s'est retrouvée tout en bas. L'outil lit inputData.json.category, obtient undefined, et retombe sur la valeur par défaut all. Toute requête renvoie alors l'assortiment complet au lieu d'une sélection. Un clic pour corriger (Schema Type → « Define using JSON Schema »), une heure et demie pour le trouver.
Bilan
Trois natures différentes d'un même problème : le modèle répond par des balises internes au lieu de texte, l'API impose une limite stricte en octets au lieu de caractères, et la configuration d'un outil se cache derrière une mauvaise valeur par défaut. Un nœud vert est la pire forme de nœud rouge — avec un échec, au moins on sait où chercher.
- 28 oct. 2026NVIDIA lance Nemotron 3.5 Super VL (120B) et les modèles Bolt pour agents locaux
- 28 oct. 2026aiedit cache certaines lignes de code à l'agent IA : 5 niveaux et 857 tests
- 28 oct. 2026vps-forge : les agents root sur VPS disposent d'un rollback automatique, les auto-bannissements passent de 7 sur 60 à 0
- 28 oct. 2026BigSleep, agent LLM, trouve 45 vulnérabilités dans V8, Fuzzilli 37 : 25% de recoupement
Questions clés sur cet article
Que sait-on sur Bug 1 : des balises de protocole internes au lieu d'une réponse humaine ?
J'ai ouvert les executions — tout est propre. Aucune exécution n'a échoué. L'hypothèse « le Reasoning Lock s'est reproduit » tombe à l'eau : le modèle répond, et vite. La seule trace dans amoCRM : la cliente a répondu « Oui » à 20h06 pour confirmer la commande (envoyée à 20h02), puis seulement une entrée dans MESSENGER-ID — silence côté message direct. J'ai ouvert la fiche OpenRouter : 871 tokens de completion, dont 766 de reasoning — il ne reste que 105 tokens visibles. En volume, ce sont des balises de protocole internes : `[VERDICT:ORDER:DATA|HOT] [NEED_MANAGER_CALL] [ST
Que sait-on sur Bug 2 : la limite de 1000 octets de Meta fragmente le texte russe ?
Après ce diagnostic, un nœud rouge est apparu sur l'envoi final : ``` NodeApiError: Bad request - please check your parameters Send direct message1 ``` L'échec survenait sur le formulaire : nom, date, domaine, téléphone, paragraphes séparés par `\n\n`. Le message le plus long jamais produit par l'agent. La documentation de l'API Meta précise : Message text must be UTF-8 and be 1000 bytes or less. Pas des caractères, des octets. En UTF-8, une lettre russe occupe 2 octets, un emoji 3 à 4 octets (✨ = 3, 💎 = 4). 1000 octets représentent environ 500 à 550 caractères de texte ru
Que sait-on sur Bug 3 : un catalogue entier au lieu d'une sélection — mauvais Schema Type ?
Alors que je réglais le problème d'envoi, une autre plainte des managers est arrivée : « l'agent montre n'importe quoi ». On demande des boucles d'oreilles, il balance la moitié du catalogue ; on demande des montres, même résultat. Le catalogue est géré par le nœud outil get_product_catalog1. L'option Specify Input Schema est activée, mais le Schema Type n'est pas défini. n8n prend la valeur par défaut — « Generate From JSON Example » : donne-moi un exemple, j'en déduis le schéma. Or dans jsonSchemaExample, il n'y avait pas un exemple de données mais le JSON Schema lui-même
Delegate Deep Research, Coding & Multi-Step Workflows to Nomad Agent
Deploy autonomous task execution with Nomad Agent (agent.nomadnet.ai) — automated web research, data synthesis, code generation, and MCP tool orchestration.
L'essentiel de l'IA — une fois par semaine
Sept actus qui ont vraiment compté, choisies à la main. Sans bruit ni communiqués.
C'est fait ! Vérifiez votre boîte mail pour la confirmation.