N8n: Switch-Node leitet stillschweigend false weiter beim Vergleich eines Boolean — „Falscher Typ: 'true' ist ein String, aber ein Boolean wurde erwartet

Ich poste das, weil es mich ein paar Stunden gekostet hat und es fehlschlägt lautlos – die schlimmste Kombination. Falls du etwas Wichtiges durch einen IF- oder Switch-Node auf Basis eines Boolean routest, lohnt sich zehn Minuten deiner Zeit dafür.

Es gibt mehrere offene Issues und Forum-Threads, die dieses Verhalten beschreiben, ohne eine Root Cause anzugeben. Das ist, was ich gefunden habe.

Das Symptom

Ich hatte einen Switch-Node, der entschied, ob eine Benachrichtigung gesendet werden sollte. Die Ausgabe des vorgelagerten Node sah so aus:

{ “shouldEscalate”: true }

Switch-Bedingung: value:

{{ $(‘Extract Response’).first().json.shouldEscalate }}
operator: Boolean → is true
Jede Ausführung routete false. Kein Fehler. Ausführung als Erfolg gemeldet. Der Alert wurde einfach nie ausgelöst.

Was es nicht war

Ich habe zunächst Zeit auf die falschen Hypothesen verschwendet, daher hier die Liste, um dir die Tour zu sparen:

•	Nicht .first() vs .item — ich habe überprüft
•	Nicht ein Multi-Run-/Item-Lineage-Problem
•	Nicht der vorgelagerte Node, der den falschen Wert produziert

Das letzte habe ich bewiesen, indem ich einen Set-Node direkt vor dem Switch platziert habe, der den rohen Wert plus seinen Typ ausgab: { “val”: true, “type”: “boolean”, “runs”: 1 }
Echter Boolean. Ein einziger Run. Korrekter Wert, der beim Node ankommt. Und der Switch sendete immer noch false.

Die eigentliche Ursache

n8n rendert {{ }} Expression-Ausgaben als Text. So kommt ein echter Boolean true beim Operator als String “true” an – und wenn es Leerzeichen am Ende oder einen Zeilenumbruch im Parameterfeld nach der schließenden }} gibt (sehr leicht beim Einfügen zu verursachen), erhältst du "true ".

Der strikte Boolean-Operator erhält "true ", das ist kein Boolean, und der Vergleich schlägt fehl. Lautlos, in die false-Richtung.

Schließlich gab der Node es wörtlich aus: Wrong type: 'true ’ is a string but was expecting a boolean

Warum die offensichtlichen Fixes nicht funktioniert haben

Ich versuchte, die Umwandlung innerhalb des Ausdrucks durchzuführen:
{{ String($(‘Extract Response’).first().json.shouldEscalate).trim().toBoolean() }}

Immer noch fehlgeschlagen. Das Leerzeichen befindet sich nicht im Ausdruck – es wird während n8ns eigenem Rendering nach }} eingeführt. Nichts, was du innerhalb der Klammern tust, kann es erreichen.

Das Umschalten von „Convert types where required

Das ist ein solides Debugging – dieser Bug ist brutal, weil er stumm fehlschlägt und grün angezeigt wird. Deine Root-Cause-Analyse: Der Wert wird von der Expression Engine in Text gerendert, und jedes beliebige Leerzeichen nach }} bricht strikte Typoperatoren. Das Trimmen innerhalb der Klammern kann es nicht beheben, da das Leerzeichen nach dem Rendering auftaucht, nicht davor.

Dein Sentinel-String-Fix ist die richtige Lösung – es ist die robusteste Möglichkeit, bei einem Boolean zu verzweigen, sobald er durch einen Expression geleitet wurde. Eine Sache, die man noch hinzufügen sollte: Du kannst auch versuchen, den Typ auf einem Set-Node upstream zu erzwingen (mit dem Type-Selector, nicht nur mit dem Expression) vor dem Switch – manchmal leitet das über n8n’s interne Coercion statt über den Text-Render-Pfad weiter. Nicht garantiert, dass es auf jeder Version funktioniert, aber billig zu testen.

Guter Catch – das ist die Art von Bug, der viel mehr kostet als die zehn Minuten, die du die Leute zum Aufwenden bittest.

Hi @Tonylw14 Willkommen!
Die Umwandlung ist nicht bedingungslos, sie tritt nur auf, wenn der Ausdruck in einem String enthalten ist. Ein Feld, das nur {{ ... }} enthält, gibt den nativen Wert zurück, und ein einzelnes Leerzeichen nach }} verwandelt das Feld in ein String-Template, daher kommt "true ". Boolean-Routing funktioniert, sobald das Feld den Ausdruck und nichts anderes enthält.
Klick in das Wertfeld, wähle alles aus, gib den Ausdruck neu ein und lasse nach }} kein Leerzeichen oder Zeilenumbruch. Um dies zu überprüfen, kopiere den Switch-Knoten und füge ihn in einen Text-Editor ein: Der Parameter sollte ={{ $('Extract Response').first().json.shouldEscalate }} lauten und bei den schließenden Klammern enden.
n8n hat dies als beabsichtigtes Verhalten bestätigt, ein Ausdruck, der in einem String eingebettet ist, wird in einen String umgewandelt, auch wenn der umgebende String nur Leerzeichen enthält:

Noch eine kleine Anmerkung für alle, die darauf stoßen: Es trifft am härtesten, wenn du Ausdrücke aus Slack/Dokumentationen/ChatGPT kopierst und einfügst, da diese ein Leerzeichen am Ende oder ein geschütztes Leerzeichen einfügen, das fast unsichtbar ist. Wenn eine Boolean-Route stumm fehlschlägt, wie Anshul sagt, ist es viel schneller, den Ausdruck von Hand zu schreiben, als zu versuchen, das Leerzeichen zu finden, indem man auf den Ausdruck starrt.

@Anshul_Namdev @ShawnWilliams — danke euch beiden. Das ist ein präziserer Mechanismus als das, was ich geschrieben habe, und es ist der Teil, bei dem ich mich geirrt habe. Ich sagte, die Umwandlung wäre bedingungslos; das ist sie nicht. Ein Feld, das nur den Ausdruck enthält, gibt den nativen Wert zurück, und alles andere — einschließlich eines einzelnen nachfolgenden Leerzeichens — macht daraus ein String-Template, das das **'true erzeugt hat. Das erklärt auch, warum das Trimmen in den geschweiften Klammern nichts bewirkt hat: Die Umwandlung findet statt, wenn das Template rendert, nachdem der Ausdruck bereits ausgewertet wurde.

@ShawnWilliams genau das ist es. Ich habe diese Ausdrücke aus einem Chat eingefügt, während ich sie aufgebaut habe. Dodelmeyer von jetzt an von Hand neu.

Aktualisiere den Beitrag mit dem korrigierten Mechanismus und dem Issue-Link.

Salman_Mehboob guter Punkt, den Typ bei einem vorgelagerten Set-Node zu erzwingen. Habe diesen Weg noch nicht versucht, werde ihn testen und mich melden.

Ich behalte die Sentinel-Zeichenkette beim sicherheitskritischen Routing aus einem Grund bei, der mit dem Fehlermodus zu tun hat und nicht mit dem Mechanismus: Ein unsichtbares nachfolgendes Leerzeichen ist in der Benutzeroberfläche nicht sichtbar, und wenn eines später erneut eingeführt wird, schlägt der Branch bei einer grünen Ausführung stillschweigend fehl. Ein Contains-Match auf ein explizites Schlüsselwort ist immun gegen diese ganze Klasse. Aber das saubere Feld ist die eigentliche Lösung und sollte zuerst kommen.