N8n : le nœud Switch achemine silencieusement false lors de la comparaison d'un booléen — « Wrong type: 'true ' is a string but was expecting a boolean »

Je poste ceci parce que cela m’a coûté quelques heures et cela échoue silencieusement, ce qui est la pire des combinaisons. Si vous routez quoi que ce soit d’important à travers un nœud IF ou Switch sur un booléen, cela vaut dix minutes de votre temps.

Il y a quelques problèmes ouverts et fils de discussion décrivant ce comportement sans cause profonde attachée. Voici ce que j’ai trouvé.

Le symptôme

J’avais un nœud Switch décidant s’il fallait envoyer une alerte. La sortie du nœud en amont ressemblait à ceci :

{ “shouldEscalate”: true }

Condition Switch : valeur :

{{ $(‘Extract Response’).first().json.shouldEscalate }}
opérateur : Booléen → est vrai
Chaque exécution a routé faux. Aucune erreur. L’exécution a été signalée comme réussie. L’alerte ne s’est tout simplement jamais déclenchée.

Ce que ce n’était pas

J’ai gaspillé du temps sur des hypothèses incorrectes d’abord, donc les voici pour vous épargner le trajet :

•	Pas .first() par rapport à .item — j'ai vérifié
•	Pas un problème de multi-exécution/lignage d'articles
•	Pas le nœud en amont produisant la mauvaise valeur

J’ai prouvé le dernier point en plaçant un nœud Set directement avant le Switch qui émettait la valeur brute plus son type : { “val”: true, “type”: “boolean”, “runs”: 1 }
Vrai booléen. Exécution unique. Valeur correcte arrivant au nœud. Et le Switch l’a toujours envoyé faux.

La cause réelle

n8n rend la sortie d’expression {{ }} sous forme de texte. Donc un vrai booléen true arrive à l’opérateur sous forme de chaîne « true » — et s’il y a un espace blanc ou une nouvelle ligne dans le champ de paramètre après }} (très facile à introduire lors du collage), vous obtenez « true ».

L’opérateur Booléen strict obtient « true », qui n’est pas un booléen, et la comparaison échoue. Silencieusement, dans la direction faux.

Eventuellement, le nœud l’a surfacé verbatim : Type incorrect : « true » est une chaîne mais attendait un booléen

Pourquoi les correctifs évidents n’ont pas fonctionné

J’ai essayé de convertir à l’intérieur de l’expression :
{{ String($(‘Extract Response’).first().json.shouldEscalate).trim().toBoolean() }}

A toujours échoué. L’espace blanc n’est pas à l’intérieur de l’expression — il est introduit après }} lors du propre rendu de n8n. Rien de ce que vous faites à l’intérieur des accolades ne peut l’atteindre.

Basculer « Convertir les types si nécessaire » change le mode d’échec plutôt que de le corriger. Dans mon cas, un paramètre routait tout vrai et l’autre routait tout faux. Les deux sont incorrects ; l’un est juste plus bruyant.

Le correctif qui fonctionne
Arrêtez de comparer le booléen. Émettez un mot-clé explicite et correspondez sur le texte :
valeur : {{ $(‘Extract Response’).first().json.shouldEscalate ? “ESCALATE” : “NORMAL” }}
opérateur : Chaîne → contient
correspondance : ESCALATE
convertir les types : désactivé

« ESCALATE » contient toujours « ESCALATE ». « NORMAL » ne le contient pas non plus de part et d’autre. L’espace blanc final devient inoffensif au lieu d’être fatal.

Vérifié dans les deux directions sur la même version — condition vrai route vrai, condition faux route faux. Cela vaut la peine de faire les deux ; tester uniquement la direction que vous attendez, c’est faire la moitié du test, et ce bug est spécifiquement capable de réussir une direction tout en échouant l’autre.

La version générale

Si un nœud Switch ou IF prend une décision qui a réellement de l’importance — alerte, escalade, filtrage de conformité, n’importe quoi où prendre silencieusement la mauvaise branche est coûteux — ne le routez pas sur un booléen brut. Rendez une chaîne sentinelle et correspondez-la.

« Le mode d’échec ici n’est pas une erreur. C’est une exécution verte qui a fait la mauvaise chose. »

C’est un débogage solide, celui-ci est brutal parce qu’il échoue silencieusement et affiche du vert. Vos pistes de cause première : la valeur est rendue en texte par le moteur d’expression, et tout espace blanc égaré après }} casse les opérateurs de type strict. Rogner à l’intérieur des accolades ne peut pas le corriger, car l’espace blanc apparaît après le rendu, pas avant.

Votre correctif de chaîne sentinelle est le bon appel, c’est la façon la plus robuste de router sur un booléen une fois qu’il a traversé une expression. Une chose qui vaut la peine d’ajouter : vous pouvez aussi essayer de forcer le type sur un nœud Set en amont (en utilisant le sélecteur de type, pas seulement l’expression) avant le Switch, parfois cela route à travers la coercition interne de n8n au lieu du chemin de rendu de texte. Pas garanti de l’esquiver sur chaque version, mais peu coûteux à tester.

Bon repérage, c’est le genre de bug qui coûte beaucoup plus que les dix minutes que vous demandez aux gens de dépenser.

Bonjour @Tonylw14 Bienvenue !
La coercition n’est pas inconditionnelle, elle ne se produit que lorsque l’expression se trouve à l’intérieur d’une chaîne de caractères. Un champ contenant uniquement {{ ... }} retourne la valeur native, et un seul espace après }} transforme le champ en modèle de chaîne, d’où provient "true ". Le routage booléen fonctionne une fois que le champ contient l’expression et rien d’autre.
Cliquez dans le champ de valeur, sélectionnez tout, retapez l’expression et ne laissez aucun espace ou saut de ligne après }}. Pour vérifier, copiez le nœud Switch et collez-le dans un éditeur de texte : le paramètre devrait se lire ={{ $('Extract Response').first().json.shouldEscalate }} et se terminer aux accolades fermantes.
n8n a confirmé que c’est un comportement intentionnel, une expression intégrée dans une chaîne de caractères est coercée en chaîne même si la chaîne environnante n’est que des espaces :

Une petite remarque pour ceux qui rencontrent ce problème : c’est particulièrement gênant quand vous copiez-collez des expressions depuis Slack/des documents/ChatGPT, car elles insèrent un espace de fin ou une espace insécable qui est presque invisible. Si une route booléenne échoue silencieusement, comme le dit Anshul, taper l’expression à la main est beaucoup plus rapide que de chercher l’espace blanc en fixant l’expression.

@Anshul_Namdev @ShawnWilliams — merci à vous deux. C’est un mécanisme plus précis que ce que j’ai écrit, et c’est la partie où je me suis trompé. J’ai dit que la coercition était inconditionnelle ; elle ne l’est pas. Un champ contenant uniquement l’expression retourne la valeur native, et tout le reste — y compris un simple espace de fin — en fait un modèle de chaîne, ce qui a produit 'true. Cela explique aussi pourquoi le rognage à l’intérieur des accolades n’a rien fait : la coercition se produit lors du rendu du modèle, après que l’expression ait déjà été évaluée.

@ShawnWilliams c’est exactement ça. J’ai collé ces expressions à partir d’un chat en construisant. Je les retaperai à la main dorénavant.

Mise à jour du post avec le mécanisme corrigé et le lien du problème.

Salman_Mehboob bonne idée de forcer le type sur un nœud Set en amont. N’avais pas essayé cette voie, je vais la tester et faire un rapport.

Je garde la chaîne de sentinel sur le routage critique pour la sécurité, pour une raison concernant le mode de défaillance plutôt que le mécanisme : un espace de fin invisible n’est pas visible dans l’interface utilisateur, et s’il est réintroduit plus tard, la branche échoue silencieusement sur une exécution verte. Une correspondance contient sur un mot-clé explicite est immunisée contre toute cette classe. Mais le champ clean est la véritable correction et devrait venir en premier.