Les nœuds de code se figent

Les nœuds de code se figent

Décrivez le problème/l’erreur/la question

Mon flux de travail fonctionne parfaitement, mais quand j’ajoute de la mémoire au LLM (OpenAI), qui se trouve presque au milieu du flux de travail, de nombreux nœuds de code qui se trouvent avant le nœud HTTP (pour envoyer la charge utile au tableau de bord)

Je rencontre un problème de performance grave où mes nœuds de code se figent et finissent par expirer, mais uniquement quand la mémoire du LLM est présente dans le flux de travail.

Mon flux de travail traite les données JSON entrantes à l’aide de quelques nœuds de code standard (effectuant le nettoyage et la validation des données). Plus loin dans le flux de travail, j’ai un nœud LLM (OpenAI) avec un composant mémoire attaché.

Quand j’exécute le flux de travail sans la mémoire du LLM, tout s’exécute parfaitement et instantanément. Cependant, dès que j’ajoute de la mémoire au LLM, plusieurs nœuds de code qui s’exécutent avant une requête HTTP se figent dans une boucle infinie.

Quel est le message d’erreur (le cas échéant) ?

Quelque chose comme ceci : il n’y a plus de message d’erreur, juste une exécution infinie. Erreur :
L’exécution de la tâche a expiré après 300 secondes
Le lanceur de tâches a pris trop de temps sur cette tâche, il a donc été suspecté d’être inactif et redémarré, et la tâche a été annulée. Vous pouvez essayer les points suivants : 1. Optimisez votre script pour éviter les tâches longues, par exemple en traitant les données par petits lots. 2. Assurez-vous que tous les chemins dans votre script peuvent se terminer, c’est-à-dire pas de boucles infinies. 3. Si votre tâche peut raisonnablement prendre plus de 300 secondes, augmentez le délai d’expiration en utilisant la variable d’environnement N8N_RUNNERS_TASK_TIMEOUT.

Informations sur votre configuration n8n

  • Version de n8n : 2.11.3
  • Base de données (par défaut : SQLite) : Par défaut
  • Paramètre n8n EXECUTIONS_PROCESS (par défaut : own, main) : Par défaut
  • Exécution de n8n via (Docker, npm, n8n cloud, application de bureau) : Docker
  • Système d’exploitation : Windows

Salut @sherazbintahir, en attendant une réponse, voici quelques ressources qui pourraient t’aider :

Ressources suggérées

Automatiquement associées à ta question.

Documentation :

Forum :

@Fabian_Hagen, @aseefdurrani, @Anshul_Namdev - vous avez déjà aidé sur des problèmes similaires, vous pouvez jeter un coup d’œil ?

Suggéré automatiquement par le bot communautaire de n8n. C’est un projet pilote - partagez vos commentaires ici.

est une protection spécifique à n8n. Cela signifie que le code JavaScript à l’intérieur de vos nœuds Code s’exécute depuis plus de 300 secondes, ce qui amène le n8n Task Runner à supposer qu’il est bloqué dans une boucle infinie ou qu’il traite une tâche impossiblement grande, il le tue donc de force.

Puisque cela ne se produit que lorsque vous attachez Memory au LLM, le composant Memory change fondamentalement la structure des données, la taille ou le comportement des données circulant dans vos nœuds Code en aval.

Lorsque Memory est attachée, le nœud LLM (ou la chaîne/l’agent dont il fait partie) peut passer l’intégralité de l’historique de conversation ou un grand objet d’état de mémoire dans le flux de données principal, plutôt que simplement la réponse finale de l’IA.

  • Le problème : Si vos nœuds Code essaient de traiter, nettoyer ou JSON.stringify() une entrée qui contient désormais des centaines ou des milliers de messages historiques, cela dépassera facilement le délai d’expiration CPU de 300 secondes.
  • La solution : Vous devez réduire les données à uniquement ce que le tableau de bord a besoin immédiatement après le nœud LLM.
    • Ajoutez un nœud Code temporaire directement après le nœud LLM avec ce code pour voir ce qui est réellement transmis :
// Vérifiez combien d'éléments et la taille des données
const items = $input.all();
console.log("Total items:", items.length);
console.log("First item keys:", Object.keys(items[0].json));
return items;
  • Si vous voyez un énorme tableau messages ou un objet de mémoire, mettez à jour vos nœuds Code en aval pour extraire uniquement le champ spécifique dont vous avez besoin (p. ex., item.json.message.content ou item.json.text) et jetez le reste avant le traitement.

Les objets Memory dans n8n (surtout lorsqu’on deal avec les intégrations LangChain) peuvent parfois contenir des références circulaires (où un objet se référence lui-même).

  • Le problème : Si vos nœuds Code utilisent JSON.stringify($input.all()) ou passent l’intégralité de l’objet d’entrée dans une fonction de parsing personnalisée, une référence circulaire peut amener le moteur JavaScript à entrer dans une boucle infinie en essayant de sérialiser l’objet, ce qui entraîne le délai d’expiration de 300 s.
  • La solution : Ne stringifiez jamais l’intégralité de $input ou $input.all() s’il contient des sorties LLM/Memory. Extrayez toujours les valeurs de chaîne primitives en premier :
  // MAUVAIS : Peut se bloquer s'il existe des références circulaires
  // const payload = JSON.stringify($input.all()); 

  // BON : Extrayez uniquement les données primitives dont vous avez besoin
  const cleanData = $input.all().map(item => ({
    response: item.json.message?.content || item.json.text,
    // ajoutez d'autres champs spécifiques ici
  }));
  const payload = JSON.stringify(cleanData);

Si vos nœuds Code utilisent des boucles while, des fonctions récursives ou des méthodes .reduce() qui dépendent de la structure du JSON entrant, l’ajout de Memory pourrait avoir modifié cette structure.

  • Le problème : Par exemple, si votre code a une boucle while qui traite un tableau jusqu’à ce qu’il soit vide, mais le composant Memory injecte accidentellement un tableau imbriqué qui se régénère ou ne respecte pas la condition de sortie, la boucle s’exécutera indéfiniment.
  • La solution : Examinez toutes les boucles while de vos nœuds Code. Ajoutez un « compteur de sécurité » pour les forcer à s’interrompre si elles dépassent un nombre raisonnable d’itérations :
let safetyCounter = 0;
while (myCondition && safetyCounter < 1000) {
    // votre logique
    safetyCounter++;
}

Cela vous aide-t-il ?

Comme je l’ai dit, la mémoire associée au LLM se trouve au milieu du flux de travail.

Et une fois que j’ai attaché la mémoire, j’ai rencontré des problèmes avec plusieurs nœuds de code, et certains nœuds de code se trouvent au début du flux de travail (avant même que le nœud de mémoire llm ne s’exécute).

Sans mémoire, le flux de travail se termine sans aucun problème, mais avec la mémoire, le même flux de travail commence à avoir un problème au nœud de code.

Comme vous ne l’aviez pas précisé clairement, je suppose que cela s’est produit après l’Agent IA

C’est plus clair. Cela ressemble à une régression de mémoire pour moi

La mise à jour vers la dernière version stable de n8n aide-t-elle ?

Salut @sherazbintahir
Les nœuds Code qui pendent uniquement de la présence d’un sous-nœud, y compris ceux qui s’exécutent avant lui, indiquent que le task runner ne peut pas résoudre le type de ce sous-nœud. Tout ce qui amène un nœud Code à demander au processus principal du contexte supplémentaire, une référence $('Node Name') ou $node["Node Name"] ou un require() d’un module externe, fait revenir le runner pour reconstruire le workflow, et un type de sous-nœud qu’il ne peut pas résoudre laisse cette demande sans réponse jusqu’à ce que le délai de 300s tue le processus. Les journaux de votre conteneur n8n afficheront « Unrecognized node type: … » au moment où il se bloque.
Réécrivez ces nœuds Code pour utiliser uniquement $input et $json, et déplacez tout ce dont ils ont besoin à partir d’un nœud antérieur sur le chemin principal avec un nœud Set d’abord. Désactiver le runner n’est pas une option sur 2.11.3, N8N_RUNNERS_ENABLED est obsolète depuis 2.0 et chaque exécution de nœud Code s’exécute sur un runner.

Merci, ça m’aidera beaucoup. Mais je suis confus ici parce que mon flux de travail complet (120+ nœuds) fonctionne parfaitement sans aucune erreur, mais quand j’attache la mémoire avec LLM (nœud d’agent IA avec OpenAI + mémoire), je rencontre ce problème.
Pour plus d’informations, dans le flux de travail sans mémoire, j’utilise le nœud OpenAI directement. Et je ne rencontre pas ce problème sur tous les nœuds de code mais sur quelques-uns.

Boîte jaune : Là où j’attache la mémoire avec LLM.
Points rouges : Là où je rencontre des problèmes de nœuds de code. La plupart des nœuds sont ceux dans lesquels je construis/prépare la charge utile à envoyer au tableau de bord.

Salut @sherazbintahir

As-tu essayé de passer à PostgreSQL pour éliminer le problème de Verrouillage de la base de données SQLite ?

services:
  postgres:
    image: postgres:16-alpine
    environment:
      - POSTGRES_USER=n8n_user
      - POSTGRES_PASSWORD=n8n_password
      - POSTGRES_DB=n8n_db
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n_user -d n8n_db"]
      interval: 5s
      timeout: 5s
      retries: 5

  n8n:
    image: n8nio/n8n:2.11.3
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n_db
      - DB_POSTGRESDB_USER=n8n_user
      - DB_POSTGRESDB_PASSWORD=n8n_password
      - EXECUTIONS_PROCESS=own
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  postgres_data:

Salut @sherazbintahir Ce n’est pas de la lenteur, tes nœuds Code sont en deadlock, pas occupés.

Depuis la v2.0, tous les nœuds Code s’exécutent dans un processus task runner séparé. Si un script utilise uniquement input/input / input/json, il reçoit une charge utile allégée et s’exécute instantanément. Mais s’il utilise ('NodeName') ou ('Node Name') ou node['Node Name'], le runner doit demander l’ensemble du workflow sérialisé à n8n, le reconstruire, et cela nécessite de résoudre chaque type de nœud sur le canvas, sous-nœuds compris. Ajoute le sous-nœud memory et le runner rencontre un type qu’il ne peut pas résoudre, la requête ne revient jamais, et ton nœud Code attend indéfiniment jusqu’à ce que le watchdog de 300 secondes le tue. Pareil que #20752.

Cela explique aussi pourquoi seulement certains de tes nœuds Code cassent — les qui fonctionnent sont ceux qui ne sortent jamais de leur propre input.

Peux-tu vérifier deux choses ?

  1. Lance-le avec memory attaché et grep tes logs :
   docker logs -f <n8n-container> | grep -i "unrecognized node type"
  1. Ouvre l’un des nœuds Code gelés — contient-il $('...') ou $node[...] quelque part ?

Si c’est oui pour les deux, le correctif est rapide : mets un nœud Set devant et passe la valeur en tant qu’expression (={{ $('Clean JSON').first().json.config }} — les expressions dans les nœuds normaux s’exécutent dans le processus principal et ne sont pas affectées), puis lis-la depuis $input à l’intérieur du nœud Code. Memory reste exactement où il est.

Poste ce que dit la ligne de log et je te donnerai la réécriture exacte pour ton nœud.

@sherazbintahir La variable n’est pas la mémoire — c’est l’échange de nœud. Sans mémoire, vous avez utilisé le nœud OpenAI simple. Pour attacher la mémoire, vous deviez basculer vers l’Agent IA, une racine de cluster LangChain qui tire les sous-nœuds sur le canevas (Chat Model, Memory, votre outil search_workwell_memory). Le nœud OpenAI se résout correctement dans le task runner ; les sous-nœuds Agent souvent ne le font pas.

Pourquoi seulement certains nœuds Code : depuis la v2.0, tous les nœuds Code s’exécutent dans un processus runner séparé.

  • Utilise uniquement $input / $json → charge utile allégée, instantané. oui (vos nœuds de nettoyage/validation)
  • Utilise $('Node') / $node['Node'] / $items() — le runner demande l’intégralité du flux de travail sérialisé et le reconstruit, ce qui signifie résoudre tous les types de nœuds sur votre canevas de 120 nœuds, sous-nœuds inclus. Un type non résolvable — la demande ne revient jamais — blocage jusqu’au déclenchement du chien de garde de 300s. non (vos générateurs de charge utile — les points rouges)

Notez que le sous-nœud outil peut aussi causer ceci seul : #20752 était postgrestool, #20132 Apify/Perplexity — là il s’est bloqué même si l’Agent ne s’était jamais exécuté.

Confirmez en 30s — laissez la mémoire attachée, figez un nœud :

// const cfg = $('Prepare Normal Formatter Evidence').first().json;
const cfg = { test: true };

S’exécute instantanément → confirmé.

Meilleure correction d’abord :

  1. Fusionnez le nœud dans le nœud Code, puis lisez $input.all(). Le plus propre à votre échelle.
  2. Définissez le nœud en avant, résolvez les valeurs en tant qu’expressions : ={{ $('Parse Extraction').first().json.score }} les expressions dans les nœuds normaux s’évaluent dans le processus principal, donc elles sont immunisées.
  3. Ignorez le nœud Code — construisez directement le corps JSON dans le nœud HTTP Request avec des expressions.

Conservez pairedItem: { item: index } sur les éléments retournés, sinon les expressions en aval réintroduisent le problème. La mémoire reste attachée dans tous les trois.

N’aidera pas : augmenter N8N_RUNNERS_TASK_TIMEOUT (l’attente est illimitée), ou N8N_RUNNERS_ENABLED=false (ignoré en 2.x).

Pouvez-vous publier la sortie de docker logs -f <n8n-container> | grep -i "unrecognized" lors de l’exécution avec l’Agent connecté ? Cela montrera si c’est la mémoire ou l’outil.