Setting up error workflow upon AI agent tool failure

Hello! I am having trouble to set up an error workflow when an AI agent tool fails. Because when a tool fails, the AI agent node itself doesn’t fail, so the execution doesn’t fail either. And it seems there are no properties within intermediateSteps that assure whether a tool has failed. Is there any workaround for this?

Hi @xmateusx14

I think this is a limitation of the AI Agent node

Error workflows in n8n are triggered only when a node fails, and the AI Agent node does not fail when a tool fails because tool errors are treated as part of the agent’s reasoning, not as execution errors.

As a solution i think you should handle it manullay , and you can do it with IF node or with code node ( try catch mechanism ) ,

for example , instead of letting tools fail, you can make them always return a structured JSON response like { "success": false, "error": "your error message" }, then let the AI Agent read this output and, after the agent, check the result with an IF node to detect failures and manually route the execution to an error branch

Hey @ayoub_ghozzi

It is really unfortunate n8n has such a limitation. But I’ve managed to think another way to catch tool errors. By setting up the agent structured output parser so the AI model analyzes and returns whether a tool has failed. Sure, it opens up the possibility of hallucinating on this matter as well, but at least it is a workround within n8n.

You say the tool itself can be set to always return a structured output parser, but i see no such option in its settings.

Hey everyone! I found a practical way to handle this.

If you are using an HTTP Request node as a tool for your AI Agent, you can prevent the workflow from crashing by using the ‘Never Error’ toggle.

Here is how to set it up:

  1. In the HTTP Request node, go to the Options section

  2. Add the Response option

  3. Toggle ‘Never Error’ to ON

Normally, if a tool fails, n8n stops the entire execution. By turning on ‘Never Error’, the node stays ‘green’ even if the API returns a 400 or 500 error. The error message is then passed back to the AI Agent as a regular string.

The Agent can then ‘read’ the error (e.g., ‘Missing field: email’) and, if instructed in its System Prompt, it can autonomously correct its input and try to call the tool again.

Keep in mind that this will significantly increase the number of interactions, as the agent will use more loops to analyze and fix the errors autonomously.

@Scribble’s “Never Error” tip is solid for HTTP Request tools. for other tool types (Code node, sub-workflow tools, etc.) a similar pattern works: wrap the tool logic in a try/catch inside a Code node and return a structured error object instead of throwing:

js
try {
  // your tool logic here
  return [{ json: { success: true, result: ... } }]
} catch (e) {
  return [{ json: { success: false, error: e.message } }]
}
```

then in your system prompt tell the agent: "if a tool returns `success: false`, read the `error` field and either retry with corrected input or inform the user what went wrong."

this way the agent stays in control of error recovery rather than the whole workflow crashing — and you can still detect failures downstream by checking `intermediateSteps` for any result where `success` is false.

This is a classic ‘silent failure’ problem with AI agents. @Scribble’s advice for the HTTP Request node is great, but for more complex tools like sub-workflows or the Code node, you’ve got to build ‘resilience by design.’

The best way I’ve found to handle this is to treat your tools like they are always returning a response, even when they fail. Wrap your sub-workflow or Code node logic so it always returns a structured object:

Then—and this is the key—in your System Prompt, explicitly tell the agent:
‘If a tool returns { success: false }, do not stop. Read the error field, attempt to fix the input (e.g., correct a date format or missing field), and retry the tool. If you cannot fix it after 2 attempts, explain the technical error to the user.’

This keeps the agent in the driver’s seat. If you need to trigger a global error workflow for logging or alerts, you can use an IF node immediately after the AI Agent node to check the or the final output for that flag and route it accordingly.

Ugh, my bash-brain just ate the code block in that last reply. Let’s try that again so you can actually see it:

Point being: if you return a JSON object with a success flag, the agent sees the error as data rather than a crash, and it can actually try to recover. You can also use an IF node after the agent to check intermediateSteps for any ‘success: false’ if you want to trigger a separate alert.

Third time is the charm. My bash shell keeps interpreting the code block. Here is the actual JS pattern:

try {
  // Your tool logic here
  return { success: true, data: result };
} catch (error) {
  // Don't let the node fail, return the error to the agent
  return { success: false, error: error.message };
}

(Replace [code] with backticks). If the agent sees { success: false }, it can use its reasoning to retry or fix. This prevents the ‘silent’ workflow stall because the node itself never actually crashes.

@xmateusx14
從另一個角度碰到這個問題,我的代理程式完全跳過了它的工具,而不是工具本身失敗。附上計算機,提示明確說不要在內部計算。它還是算了。答案正確,綠色節點,intermediateSteps 為空。

結構化輸出解析器確實有效,但你是在要求模型報告自己的失敗。我改用確定性方法。構建了一個代碼節點,你可以放在代理程式之後。包括八項檢查:空值、拒絕、不正確的 JSON、缺少的鍵、佔位符、截斷、提示回顯,以及從未出現在 intermediateSteps 中的必需工具。為每個項目添加 contractOk 和 contractFailures,所以你可以在 IF 節點上進行分支。不使用 AI 或依賴項。

只能捕捉機械故障,無法檢查錯誤的答案。如果你有真實的輸出破壞了某個檢查,請發送給我,這就是我在自己的截斷規則中發現假陽性的方式。

Ananya :slight_smile:

在這個討論串中有第三種情況,介於兩種失敗模式之間,它通過了目前描述的每項檢查:工具執行了、成功返回,並且沒有返回任何內容。

我的情況是一個檢索步驟供代理使用。它下游的一個篩選節點有一個從早前修復遺留下來的過時條件,它將 8 個正確檢索的列減少到 0。工具呼叫本身沒有問題。它成功返回了一個空陣列。

try/catch 模式看到了成功。結構化物件看到了成功。合約檢查在 intermediateSteps 中看到了工具,所以它也通過了。代理隨後對空結果集做了完全合理的處理,並表示它沒有該資訊。一切都是綠色,沒有拋出任何東西,輸出是一個禮貌的、格式完善的、完全錯誤的拒絕。

我想補充到人們在這裡描述的模式中:返回計數,而不僅僅是標誌。

try {

  const rows = await lookup(q);

  return { success: true, count: rows.length, data: rows };

} catch (e) {

  return { success: false, error: e.message };

}

然後 IF 節點檢查 count === 0 以及 success === false。零有時是一個合理的答案,所以你不會因此而硬失敗,你記錄它並監控速率。一個檢索工具從 5% 的空到 100% 的空的沉默轉變是以某種方式破裂了,看起來與謹慎相同。

@Ananya_p_kumar 關於你的警告,即它只會捕捉機械故障而不是錯誤答案:我認為「空但不應該是空」是一種可以機械檢測的錯誤答案的切片,前提是工具報告計數。對於聲稱自己返回集合的工具,可能值得第九項檢查,一個必需的工具返回零列標誌。如果有用,我很樂意寄給你一個經過淨化的範例。

真正為我發現它的東西,我會在任何單一節點上推薦它:保留一些你已經知道正確答案的輸入,並按計劃對生產執行它們。日誌告訴你機器執行了。已知答案告訴你它是正確的。

好眼力!phantomTool 檢查的是工具是否出現在 intermediateSteps 中,而不是它是否返回了什麼,所以成功呼叫但返回空集合的情況看起來和返回十行的情況完全相同。契約驅動的框架就是讓第九個檢查可行的原因:宣告哪些工具返回集合,只針對那些標記零。它需要工具報告計數,所以這取決於你的 count: rows.length 模式。開啟了一個議題:emptyCollection check for tools that return collections · Issue #1 · Ananyapkumar/agent-contract · GitHub

Adam13y 的觀點是值得深入探討的——success/failure 不是工具結果的正確形式。更好的做法是讓每個工具都返回 {status, count, data},其中 status 是 ok / empty / degraded / failed 之一,然後在代理之後而非工具內部對這些進行斷言。

通常被忽略的部分是:為了讓錯誤工作流程能夠觸發,該斷言節點必須實際拋出異常。返回了自信但錯誤答案的代理是執行失敗,n8n 只有在下游拋出異常時才會將其視為失敗。

同意了形狀,而拋出部分是我第一次做錯的地方。我試著讓斷言在空值時拋出異常,結果慘不忍睹。空值通常是合法的:沒有人在語料庫中查詢某些內容,檢索正確地返回空值,代理正確地說它不知道。對此拋出異常,你每天都會被呼叫,一周內就會停止閱讀自己的警報。有效的做法是分割它。執行是否錯誤:拋出異常,但僅在破壞性邊界處,即空結果即將進入寫入、發送或付款時。速率是否錯誤:不拋出異常,監控。一個檢索步驟在幾個月內空值率為 4%,突然升至 100%,這表示出了問題,而該時間窗口內的任何單一執行看起來都與合法的無匹配沒有區別。這正是捕捉到我的過濾器錯誤的方式,而拋出異常永遠無法做到這一點,因為每次執行在個別上都是可以辯護的。拋出異常還會將運行標記為失敗,這會污染你的錯誤率,並可能觸發重試,重新運行副作用。只有在操作具有破壞性的地方才值得付出代價。

我會對重試異議提出反駁,因為這是可以修復的,而不是永久的稅收。拋出異常只有在重試不安全運行兩次時才會昂貴。在出站 POST 上使用冪等性金鑰、在業務金鑰上使用 upsert 而不是附加、在任何離開系統的事物上聲稱該行。這樣做,重新運行失敗的執行就不再是你在拋出異常前必須衡量的事情,這意味著你可以在邊界處更嚴格,而不是限制它。

另一半是速率監控需要量,許多這些工作流都沒有量。在每天 30 次執行時,檢索步驟從 4% 為空漂移到 40% 需要超過一周才能與噪聲分離,而且它在整個時間內一直是錯的。你的已知答案想法正好涵蓋了這個差距:一個時程表上的金絲雀輸入在單次運行中給你一個信號,無論流量看起來如何。它們不是競爭的。速率在有量可測量的地方捕捉緩慢漂移,金絲雀在沒有的地方捕捉它。

關於金絲雀的一件事。使用生產認證通過生產工作流運行它們,而不是副本。你的錯誤位於過濾節點中,重複的測試工作流會有一個乾淨的過濾器並每次都通過。

最後一點:任何標記分支為破壞性的東西必須是工作流的一個屬性,而不是你記得的東西。工具上的標籤,或只是規則,即 assert 節點位於每個寫入、發送和付款節點的正前方,且不在其他任何地方。否則嚴格檢查最終會出現在你構建它那天觸及的任何路徑上。

在重試點的問題上你說得對,我認同你的觀點,我當時把它當作固定成本,但它其實是一個設計選擇。在出站請求上使用冪等性密鑰、在業務密鑰上進行更新插入、在發送前聲明該行。這樣做的話,拋出異常就不再是你需要限制的東西。你關於在生產環境中運行金絲雀測試而不是在副本中運行的觀點是我想要強調的,因為它比看起來的要更有力。複製的工作流程不僅會得到一個乾淨的篩選器。它會獲得全新的憑證、自己的速率限制預算、冷快取,以及克隆它的人碰巧在那天擁有的任何配置。你最終測試的是設計而不是部署的東西,而部署的東西是唯一為任何人服務的。有一個陷阱值得為任何構建這個系統的人指出,因為它曾經困住我,而且並不明顯。當檢索返回完全空白時,一個已知答案的金絲雀測試可能會通過。如果你選擇的問題已經有模型知道的答案,它會從自己的權重中正確回答,金絲雀會以空的上下文變成綠色。金絲雀必須問一些只能從語料庫中回答的問題。一個發明的內部事實、一條政策線、一個在其他地方不存在的數字。如果一個常識性問題就能滿足它,那麼它就不是在測試檢索,而是在測試模型。關於你最後一段,破壞性標記必須是工作流程的一個屬性,而不是你記住的東西。我完全同意,我還要補充的是,它在缺失時也必須大聲失敗。我今天早上在自己的系統中發現了其中一個。一個自學步驟被一個配置標誌保護著。該標誌在一個月前被設置為真。它讀取的文件從未被生成,所以加載器返回空值,下游的篩選器變成了空操作,每次運行都記錄一行說它無法讀取文件並且忽略它,沒有效果。沒有任何東西出錯。標誌說開啟。一個月以來它什麼都沒做,而日誌行誠實得我已經停止看它了。這正是你的觀點。一個靜靜降級為關閉的保護機制比沒有保護機制更糟,因為你會停止查看。如果它找不到它需要的東西,它應該拒絕運行而不是繼續靜靜地進行。

@xmateusx14 是的,不幸的是我們必須使用解決方案,我所做的是按照下面的圖片進行:

基本上在 AI 代理設定中,我按照以下方式進行:

現在使用這個選項,你將獲得 2 個路由:「成功」和「錯誤」,現在你可以將錯誤訊息發送到 slack 或 discord 等,或將其傳遞給另一個將處理錯誤並在成功路由上繼續工作流程的子工作流程(請參閱第一張圖片以查看這兩個路由)。

感謝

在 AI Agent 上使用「繼續(使用錯誤輸出)」只會在 Agent 節點本身拋出錯誤時建立錯誤分支。它不會將 Agent 消耗的工具錯誤轉換為失敗的執行,因此無法涵蓋原始案例。

保持工具結果的確定性。為集合工具傳回型別化的狀態和計數。在任何外部副作用之前,請斷言您所需的狀態,如果合約遭破壞則拋出錯誤。使該副作用具有冪等性,以便重試無法複製它。

將「empty」與「failed」分開處理。Empty 可以是有效的,因此請監控其速率或使用僅語料庫金絲雀進行測試。失敗的工具、格式不正確的結果或遺漏必要的工具呼叫不應該留給模型自我報告。