N8n:Switch 節點在比較布林值時無聲地路由 false — "錯誤的類型:'true ' 是字符串但預期為布林值"

我發佈這個是因為它花了我幾個小時,而且它無聲地失敗,這是最糟糕的組合。如果你通過 IF 或 Switch 節點路由任何重要的東西在布爾值上,這值得你花十分鐘的時間。

有一些開放的問題和論壇主題描述這種行為,但沒有附加根本原因。這是我發現的。

症狀

我有一個 Switch 節點決定是否發送警報。上游節點的輸出看起來像這樣:

{ “shouldEscalate”: true }

Switch condition: value:

{{ $(‘Extract Response’).first().json.shouldEscalate }}
operator: Boolean → is true
每次執行都路由為 false。沒有錯誤。執行報告成功。警報就是永遠不會觸發。

不是什麼

我一開始在錯誤的假設上浪費了時間,所以這裡列出來為了節省你的時間:

•	不是 .first() 對比 .item — 我檢查過了
•	不是多運行/項目系統問題
•	不是上游節點產生錯誤的值

我通過在 Switch 前直接放置一個 Set 節點來證明最後一點,該節點發出原始值加其類型:{ “val”: true, “type”: “boolean”, “runs”: 1 }
真正的布爾值。單次運行。正確的值到達節點。Switch 仍然發送它為 false。

真正的原因

n8n 將 {{ }} 表達式輸出呈現為文字。所以真實的布爾值 true 以字符串 “true” 的形式到達運算符 — 如果在參數欄位中的結束 }} 之後有任何尾部空格或換行符(粘貼時很容易引入),你會得到 "true "。

嚴格的 Boolean 運算符得到 "true ",這不是布爾值,比較失敗。靜默地,向 false 方向失敗。

最終節點逐字表面它: Wrong type: 'true ’ is a string but was expecting a boolean

為什麼明顯的修復沒有起作用

我嘗試在表達式內轉換:
{{ String($(‘Extract Response’).first().json.shouldEscalate).trim().toBoolean() }}

仍然失敗。空格不在表達式內 — 它在 n8n 自己的呈現過程中在 }} 之後引入。你在大括號內做的任何事都無法到達它。

切換「在需要時轉換類型」會改變失敗模式而不是修復它。在我的情況下,一個設置將所有內容路由為 true,另一個將所有內容路由為 false。兩者都是錯誤的;一個只是更明顯。

有效的修復
停止比較布爾值。發出一個明確的關鍵字並在文字上匹配:
value: {{ $(‘Extract Response’).first().json.shouldEscalate ? “ESCALATE” : “NORMAL” }}
operator: String → contains
match: ESCALATE
convert types: off

"ESCALATE " 仍然包含 “ESCALATE”。"NORMAL " 無論哪種方式都不包含它。尾部空格變成無害的而不是致命的。

在同一個構建上的兩個方向上驗證 — 條件 true 路由 true,條件 false 路由 false。值得雙向測試;僅測試你期望的方向是半個測試,這個 bug 特別能夠在一個方向上通過而在另一個方向上失敗。

通用版本

如果 Switch 或 IF 節點做出的決定真的很重要 — 警報、升級、合規門控、任何靜默走錯分支代價高昂的地方 — 不要在原始布爾值上路由它。呈現一個標記字符串並在其上匹配。

「這裡的失敗模式不是錯誤。它是做了錯誤事情的綠色執行。」

這個除錯超級麻煩,因為它會無聲地失敗並顯示綠色。你的根本原因追蹤:該值被表達式引擎渲染為文本,而 }} 後面的任何多餘空白都會破壞嚴格類型運算符。修剪大括號內部無法修復它,因為空白是在渲染之後出現,而不是之前。

你的哨兵字符串修復是正確的做法,它是通過表達式後在布林值上進行路由的最穩健方式。值得添加一件事:你也可以嘗試在 Switch 之前在上游的 Set 節點上強制類型(使用類型選擇器,而不只是表達式),有時這會通過 n8n 的內部強制類型而不是文本渲染路徑進行路由。不保證在每個版本上都能避免,但測試成本很低。

抓得好,這是那種成本遠超過你要求人們花費的十分鐘的錯誤。

@Tonylw14 歡迎!
強制轉換並非無條件的,只有當表達式位於字符串內時才會發生。只包含 {{ ... }} 的字段會返回原生值,而 }} 後面加一個空格會將字段轉換為字符串模板,這就是 "true " 的來源。布爾路由在字段只包含表達式且沒有其他內容時才能運作。
點擊進值字段,全選,重新輸入表達式,並且在 }} 後面不要留下空格或換行符。要驗證,複製 Switch 節點並將其貼到文本編輯器中:參數應該顯示 ={{ $('Extract Response').first().json.shouldEscalate }} 並在右括號處結束。
n8n 已確認這是預期行為,嵌入在字符串中的表達式會被強制轉換為字符串,即使周圍的字符串只是空白字符:

對於遇到這個問題的任何人,有一個小提示:當你從 Slack/文檔/ChatGPT 複製貼上表達式時,問題最為嚴重,因為它們會插入尾部空格或幾乎看不見的不間斷空格。如果布林路由無聲地失敗,就像 Anshul 說的那樣,手動寫出表達式比盯著表達式尋找空白字符要快得多。

@Anshul_Namdev @ShawnWilliams — 感謝你們兩位。這是比我寫的更敏銳的機制,也是我理解錯誤的部分。我說強制轉換是無條件的;其實不是。只持有表達式的字段會返回原生值,而其他任何情況——包括單個尾隨空格——都會使其成為字符串模板,這就是產生 'true 的原因。這也解釋了為什麼在大括號內修剪沒有起作用:強制轉換發生在模板渲染時,在表達式已經求值之後。

@ShawnWilliams 正是這樣。我從建構時的聊天中粘貼了這些表達式。今後改為手動重新輸入。

更新帖子,提供了更正後的機制和問題鏈接。

Salman_Mehboob 在上游 Set 節點上強制轉換類型的建議不錯。還沒試過那條路,我會測試一下並匯報結果。

我在安全關鍵路由上保留了哨兵字符串,原因是關於故障模式而不是機制:不可見的尾隨空格在用戶界面中看不見,如果稍後重新引入一個,分支會在綠色執行上無聲地失敗。對顯式關鍵字的包含匹配能免疫整個問題類別。但清潔字段是實際的修復,應該首先進行。