Handle FatalError in DurableAgent tool execution - #376

Merged
pranaygp merged 5 commits into
mainfrom
copilot/handle-tool-call-errors
Nov 20, 2025
Merged

Handle FatalError in DurableAgent tool execution#376
pranaygp merged 5 commits into
mainfrom
copilot/handle-tool-call-errors

Conversation

CopilotAI commented Nov 20, 2025

Copy link
Copy Markdown
Contributor

FatalError thrown in DurableAgent tool execution currently kills the workflow. It should convert to a tool error result sent back to the LLM, matching AI SDK behavior.

Changes

  • packages/ai/src/agent/durable-agent.ts: Wrap tool execution in try-catch. Convert FatalError to error-text tool result. Re-throw other errors for retry.
  • packages/ai/src/agent/durable-agent.test.ts: Add tests for error handling scenarios.

Example

constagent=newDurableAgent({model: 'anthropic/claude-opus',tools: {getTool: {description: 'Get data',inputSchema: z.object({id: z.string()}),execute: async({ id })=>{// Previously: kills workflow// Now: error sent to LLM as tool resultthrownewFatalError('Invalid ID');},},},});

The LLM receives the error as a tool result and can retry with different parameters or inform the user.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • fonts.googleapis.com
    • Triggering command: /usr/local/bin/node /usr/local/bin/node /home/REDACTED/work/workflow/workflow/node_modules/.pnpm/next@15.5.4_@opentelemetry+api@1.9.0_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/jest-worker/processChild.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

View original Slack conversation


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@changeset-bot

changeset-botBot commented Nov 20, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6e61ab9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
@workflow/aiPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Nov 20, 2025

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentPreviewCommentsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreviewCommentNov 20, 2025 10:19pm
example-nextjs-workflow-webpackReadyReadyPreviewCommentNov 20, 2025 10:19pm
example-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-express-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-hono-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-nitro-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-nuxt-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-sveltekit-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-vite-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workflow-docsReadyReadyPreviewCommentNov 20, 2025 10:19pm

Convert FatalError exceptions in tool execution to tool-result errors that get
propagated back to the LLM, mimicking AI SDK behavior. Non-fatal errors are
re-thrown to allow workflow retry mechanisms to handle them.
- Updated executeTool to catch FatalError and convert to error-text tool result
- Added comprehensive tests for error handling scenarios
- All tests passing
Co-authored-by: pranaygp <1797812+pranaygp@users.noreply.github.com>
Remove unnecessary type assertion and add changeset for the DurableAgent fix
Co-authored-by: pranaygp <1797812+pranaygp@users.noreply.github.com>
Comment threadpackages/ai/src/agent/durable-agent.ts Outdated
@VaguelySerious
VaguelySerious marked this pull request as ready for review November 20, 2025 21:01
Comment thread.changeset/handle-tool-errors.md Outdated
@mpaicoding-byte

Copy link
Copy Markdown

Ideally it should stop the workflow right? It could be an issue with an api call in that step or some code logic in that step. So when that is fixed, then it should resume from that step completion. Wondering what was the intent behind this logic or is there any another way to achieve this?

@VaguelySerious

Copy link
Copy Markdown
Member

@pranaygp Good point from @mpaicoding-byte, FatalError is a user-thrown error and should intentionally kill the workflow

@pranaygp

Copy link
Copy Markdown
Contributor

the thinking here was (we can revisit this) -

  • RetryableError (or any error) allows the workflow system to deterministically retry
  • FatalError bubbles into the LLM since the LLM was the invokeR of the function and the LLM needs to know the status of the tool call (success/failed state)

this maps to throwing errors in tools in AI sdk. when a tool throws an error it doesn't kill the entire generateText/streamText loop - it returns the failed tool call as an error response back to the LLM

@VaguelySerious

Copy link
Copy Markdown
Member

Ah, I remember. That too makes sense, though I can see how from the user's perspective, not being able to intentionally throw any error in a step (tool) that terminates the agent is counterintuitive

@mpaicoding-byte

Copy link
Copy Markdown

Thanks @VaguelySerious@pranaygp for looking at this. It could also be unintentional, maybe there is no try catch implemented properly and there was some fatal error from an api or some function, that ideally should be considered as a system error and the step should be retried and eventually the error should be propogated to workflow IMO

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mpaicoding-byte@VaguelySerious@pranaygp
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Handle FatalError in DurableAgent tool execution - #376

Merged
pranaygp merged 5 commits into
mainfrom
copilot/handle-tool-call-errors
Nov 20, 2025
Merged

Handle FatalError in DurableAgent tool execution#376
pranaygp merged 5 commits into
mainfrom
copilot/handle-tool-call-errors

Conversation

CopilotAI commented Nov 20, 2025

Copy link
Copy Markdown
Contributor

FatalError thrown in DurableAgent tool execution currently kills the workflow. It should convert to a tool error result sent back to the LLM, matching AI SDK behavior.

Changes

  • packages/ai/src/agent/durable-agent.ts: Wrap tool execution in try-catch. Convert FatalError to error-text tool result. Re-throw other errors for retry.
  • packages/ai/src/agent/durable-agent.test.ts: Add tests for error handling scenarios.

Example

constagent=newDurableAgent({model: 'anthropic/claude-opus',tools: {getTool: {description: 'Get data',inputSchema: z.object({id: z.string()}),execute: async({ id })=>{// Previously: kills workflow// Now: error sent to LLM as tool resultthrownewFatalError('Invalid ID');},},},});

The LLM receives the error as a tool result and can retry with different parameters or inform the user.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • fonts.googleapis.com
    • Triggering command: /usr/local/bin/node /usr/local/bin/node /home/REDACTED/work/workflow/workflow/node_modules/.pnpm/next@15.5.4_@opentelemetry&#43;api@1.9.0_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/jest-worker/processChild.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

View original Slack conversation


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@changeset-bot

changeset-botBot commented Nov 20, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6e61ab9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
@workflow/aiPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Nov 20, 2025

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentPreviewCommentsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreviewCommentNov 20, 2025 10:19pm
example-nextjs-workflow-webpackReadyReadyPreviewCommentNov 20, 2025 10:19pm
example-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-express-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-hono-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-nitro-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-nuxt-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-sveltekit-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-vite-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workflow-docsReadyReadyPreviewCommentNov 20, 2025 10:19pm

Convert FatalError exceptions in tool execution to tool-result errors that get
propagated back to the LLM, mimicking AI SDK behavior. Non-fatal errors are
re-thrown to allow workflow retry mechanisms to handle them.
- Updated executeTool to catch FatalError and convert to error-text tool result
- Added comprehensive tests for error handling scenarios
- All tests passing
Co-authored-by: pranaygp <1797812+pranaygp@users.noreply.github.com>
Remove unnecessary type assertion and add changeset for the DurableAgent fix
Co-authored-by: pranaygp <1797812+pranaygp@users.noreply.github.com>
Comment threadpackages/ai/src/agent/durable-agent.ts Outdated
@VaguelySerious
VaguelySerious marked this pull request as ready for review November 20, 2025 21:01
Comment thread.changeset/handle-tool-errors.md Outdated
@mpaicoding-byte

Copy link
Copy Markdown

Ideally it should stop the workflow right? It could be an issue with an api call in that step or some code logic in that step. So when that is fixed, then it should resume from that step completion. Wondering what was the intent behind this logic or is there any another way to achieve this?

@VaguelySerious

Copy link
Copy Markdown
Member

@pranaygp Good point from @mpaicoding-byte, FatalError is a user-thrown error and should intentionally kill the workflow

@pranaygp

Copy link
Copy Markdown
Contributor

the thinking here was (we can revisit this) -

  • RetryableError (or any error) allows the workflow system to deterministically retry
  • FatalError bubbles into the LLM since the LLM was the invokeR of the function and the LLM needs to know the status of the tool call (success/failed state)

this maps to throwing errors in tools in AI sdk. when a tool throws an error it doesn't kill the entire generateText/streamText loop - it returns the failed tool call as an error response back to the LLM

@VaguelySerious

Copy link
Copy Markdown
Member

Ah, I remember. That too makes sense, though I can see how from the user's perspective, not being able to intentionally throw any error in a step (tool) that terminates the agent is counterintuitive

@mpaicoding-byte

Copy link
Copy Markdown

Thanks @VaguelySerious@pranaygp for looking at this. It could also be unintentional, maybe there is no try catch implemented properly and there was some fatal error from an api or some function, that ideally should be considered as a system error and the step should be retried and eventually the error should be propogated to workflow IMO

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mpaicoding-byte@VaguelySerious@pranaygp
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Handle FatalError in DurableAgent tool execution - #376

Merged
pranaygp merged 5 commits into
mainfrom
copilot/handle-tool-call-errors
Nov 20, 2025
Merged

Handle FatalError in DurableAgent tool execution#376
pranaygp merged 5 commits into
mainfrom
copilot/handle-tool-call-errors

Conversation

CopilotAI commented Nov 20, 2025

Copy link
Copy Markdown
Contributor

FatalError thrown in DurableAgent tool execution currently kills the workflow. It should convert to a tool error result sent back to the LLM, matching AI SDK behavior.

Changes

  • packages/ai/src/agent/durable-agent.ts: Wrap tool execution in try-catch. Convert FatalError to error-text tool result. Re-throw other errors for retry.
  • packages/ai/src/agent/durable-agent.test.ts: Add tests for error handling scenarios.

Example

constagent=newDurableAgent({model: 'anthropic/claude-opus',tools: {getTool: {description: 'Get data',inputSchema: z.object({id: z.string()}),execute: async({ id })=>{// Previously: kills workflow// Now: error sent to LLM as tool resultthrownewFatalError('Invalid ID');},},},});

The LLM receives the error as a tool result and can retry with different parameters or inform the user.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • fonts.googleapis.com
    • Triggering command: /usr/local/bin/node /usr/local/bin/node /home/REDACTED/work/workflow/workflow/node_modules/.pnpm/next@15.5.4_@opentelemetry&#43;api@1.9.0_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/jest-worker/processChild.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

View original Slack conversation


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@changeset-bot

changeset-botBot commented Nov 20, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6e61ab9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
@workflow/aiPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Nov 20, 2025

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentPreviewCommentsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreviewCommentNov 20, 2025 10:19pm
example-nextjs-workflow-webpackReadyReadyPreviewCommentNov 20, 2025 10:19pm
example-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-express-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-hono-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-nitro-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-nuxt-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-sveltekit-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-vite-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workflow-docsReadyReadyPreviewCommentNov 20, 2025 10:19pm

Convert FatalError exceptions in tool execution to tool-result errors that get
propagated back to the LLM, mimicking AI SDK behavior. Non-fatal errors are
re-thrown to allow workflow retry mechanisms to handle them.
- Updated executeTool to catch FatalError and convert to error-text tool result
- Added comprehensive tests for error handling scenarios
- All tests passing
Co-authored-by: pranaygp <1797812+pranaygp@users.noreply.github.com>
Remove unnecessary type assertion and add changeset for the DurableAgent fix
Co-authored-by: pranaygp <1797812+pranaygp@users.noreply.github.com>
Comment threadpackages/ai/src/agent/durable-agent.ts Outdated
@VaguelySerious
VaguelySerious marked this pull request as ready for review November 20, 2025 21:01
Comment thread.changeset/handle-tool-errors.md Outdated
@mpaicoding-byte

Copy link
Copy Markdown

Ideally it should stop the workflow right? It could be an issue with an api call in that step or some code logic in that step. So when that is fixed, then it should resume from that step completion. Wondering what was the intent behind this logic or is there any another way to achieve this?

@VaguelySerious

Copy link
Copy Markdown
Member

@pranaygp Good point from @mpaicoding-byte, FatalError is a user-thrown error and should intentionally kill the workflow

@pranaygp

Copy link
Copy Markdown
Contributor

the thinking here was (we can revisit this) -

  • RetryableError (or any error) allows the workflow system to deterministically retry
  • FatalError bubbles into the LLM since the LLM was the invokeR of the function and the LLM needs to know the status of the tool call (success/failed state)

this maps to throwing errors in tools in AI sdk. when a tool throws an error it doesn't kill the entire generateText/streamText loop - it returns the failed tool call as an error response back to the LLM

@VaguelySerious

Copy link
Copy Markdown
Member

Ah, I remember. That too makes sense, though I can see how from the user's perspective, not being able to intentionally throw any error in a step (tool) that terminates the agent is counterintuitive

@mpaicoding-byte

Copy link
Copy Markdown

Thanks @VaguelySerious@pranaygp for looking at this. It could also be unintentional, maybe there is no try catch implemented properly and there was some fatal error from an api or some function, that ideally should be considered as a system error and the step should be retried and eventually the error should be propogated to workflow IMO

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mpaicoding-byte@VaguelySerious@pranaygp
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Handle FatalError in DurableAgent tool execution - #376

Merged
pranaygp merged 5 commits into
mainfrom
copilot/handle-tool-call-errors
Nov 20, 2025
Merged

Handle FatalError in DurableAgent tool execution#376
pranaygp merged 5 commits into
mainfrom
copilot/handle-tool-call-errors

Conversation

CopilotAI commented Nov 20, 2025

Copy link
Copy Markdown
Contributor

FatalError thrown in DurableAgent tool execution currently kills the workflow. It should convert to a tool error result sent back to the LLM, matching AI SDK behavior.

Changes

  • packages/ai/src/agent/durable-agent.ts: Wrap tool execution in try-catch. Convert FatalError to error-text tool result. Re-throw other errors for retry.
  • packages/ai/src/agent/durable-agent.test.ts: Add tests for error handling scenarios.

Example

constagent=newDurableAgent({model: 'anthropic/claude-opus',tools: {getTool: {description: 'Get data',inputSchema: z.object({id: z.string()}),execute: async({ id })=>{// Previously: kills workflow// Now: error sent to LLM as tool resultthrownewFatalError('Invalid ID');},},},});

The LLM receives the error as a tool result and can retry with different parameters or inform the user.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • fonts.googleapis.com
    • Triggering command: /usr/local/bin/node /usr/local/bin/node /home/REDACTED/work/workflow/workflow/node_modules/.pnpm/next@15.5.4_@opentelemetry&#43;api@1.9.0_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/jest-worker/processChild.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

View original Slack conversation


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@changeset-bot

changeset-botBot commented Nov 20, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6e61ab9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
@workflow/aiPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Nov 20, 2025

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentPreviewCommentsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreviewCommentNov 20, 2025 10:19pm
example-nextjs-workflow-webpackReadyReadyPreviewCommentNov 20, 2025 10:19pm
example-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-express-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-hono-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-nitro-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-nuxt-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-sveltekit-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-vite-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workflow-docsReadyReadyPreviewCommentNov 20, 2025 10:19pm

Convert FatalError exceptions in tool execution to tool-result errors that get
propagated back to the LLM, mimicking AI SDK behavior. Non-fatal errors are
re-thrown to allow workflow retry mechanisms to handle them.
- Updated executeTool to catch FatalError and convert to error-text tool result
- Added comprehensive tests for error handling scenarios
- All tests passing
Co-authored-by: pranaygp <1797812+pranaygp@users.noreply.github.com>
Remove unnecessary type assertion and add changeset for the DurableAgent fix
Co-authored-by: pranaygp <1797812+pranaygp@users.noreply.github.com>
Comment threadpackages/ai/src/agent/durable-agent.ts Outdated
@VaguelySerious
VaguelySerious marked this pull request as ready for review November 20, 2025 21:01
Comment thread.changeset/handle-tool-errors.md Outdated
@mpaicoding-byte

Copy link
Copy Markdown

Ideally it should stop the workflow right? It could be an issue with an api call in that step or some code logic in that step. So when that is fixed, then it should resume from that step completion. Wondering what was the intent behind this logic or is there any another way to achieve this?

@VaguelySerious

Copy link
Copy Markdown
Member

@pranaygp Good point from @mpaicoding-byte, FatalError is a user-thrown error and should intentionally kill the workflow

@pranaygp

Copy link
Copy Markdown
Contributor

the thinking here was (we can revisit this) -

  • RetryableError (or any error) allows the workflow system to deterministically retry
  • FatalError bubbles into the LLM since the LLM was the invokeR of the function and the LLM needs to know the status of the tool call (success/failed state)

this maps to throwing errors in tools in AI sdk. when a tool throws an error it doesn't kill the entire generateText/streamText loop - it returns the failed tool call as an error response back to the LLM

@VaguelySerious

Copy link
Copy Markdown
Member

Ah, I remember. That too makes sense, though I can see how from the user's perspective, not being able to intentionally throw any error in a step (tool) that terminates the agent is counterintuitive

@mpaicoding-byte

Copy link
Copy Markdown

Thanks @VaguelySerious@pranaygp for looking at this. It could also be unintentional, maybe there is no try catch implemented properly and there was some fatal error from an api or some function, that ideally should be considered as a system error and the step should be retried and eventually the error should be propogated to workflow IMO

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mpaicoding-byte@VaguelySerious@pranaygp
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Handle FatalError in DurableAgent tool execution - #376

Merged
pranaygp merged 5 commits into
mainfrom
copilot/handle-tool-call-errors
Nov 20, 2025
Merged

Handle FatalError in DurableAgent tool execution#376
pranaygp merged 5 commits into
mainfrom
copilot/handle-tool-call-errors

Conversation

CopilotAI commented Nov 20, 2025

Copy link
Copy Markdown
Contributor

FatalError thrown in DurableAgent tool execution currently kills the workflow. It should convert to a tool error result sent back to the LLM, matching AI SDK behavior.

Changes

  • packages/ai/src/agent/durable-agent.ts: Wrap tool execution in try-catch. Convert FatalError to error-text tool result. Re-throw other errors for retry.
  • packages/ai/src/agent/durable-agent.test.ts: Add tests for error handling scenarios.

Example

constagent=newDurableAgent({model: 'anthropic/claude-opus',tools: {getTool: {description: 'Get data',inputSchema: z.object({id: z.string()}),execute: async({ id })=>{// Previously: kills workflow// Now: error sent to LLM as tool resultthrownewFatalError('Invalid ID');},},},});

The LLM receives the error as a tool result and can retry with different parameters or inform the user.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • fonts.googleapis.com
    • Triggering command: /usr/local/bin/node /usr/local/bin/node /home/REDACTED/work/workflow/workflow/node_modules/.pnpm/next@15.5.4_@opentelemetry&#43;api@1.9.0_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/jest-worker/processChild.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

View original Slack conversation


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@changeset-bot

changeset-botBot commented Nov 20, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6e61ab9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
@workflow/aiPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Nov 20, 2025

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentPreviewCommentsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreviewCommentNov 20, 2025 10:19pm
example-nextjs-workflow-webpackReadyReadyPreviewCommentNov 20, 2025 10:19pm
example-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-express-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-hono-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-nitro-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-nuxt-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-sveltekit-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-vite-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workflow-docsReadyReadyPreviewCommentNov 20, 2025 10:19pm

Convert FatalError exceptions in tool execution to tool-result errors that get
propagated back to the LLM, mimicking AI SDK behavior. Non-fatal errors are
re-thrown to allow workflow retry mechanisms to handle them.
- Updated executeTool to catch FatalError and convert to error-text tool result
- Added comprehensive tests for error handling scenarios
- All tests passing
Co-authored-by: pranaygp <1797812+pranaygp@users.noreply.github.com>
Remove unnecessary type assertion and add changeset for the DurableAgent fix
Co-authored-by: pranaygp <1797812+pranaygp@users.noreply.github.com>
Comment threadpackages/ai/src/agent/durable-agent.ts Outdated
@VaguelySerious
VaguelySerious marked this pull request as ready for review November 20, 2025 21:01
Comment thread.changeset/handle-tool-errors.md Outdated
@mpaicoding-byte

Copy link
Copy Markdown

Ideally it should stop the workflow right? It could be an issue with an api call in that step or some code logic in that step. So when that is fixed, then it should resume from that step completion. Wondering what was the intent behind this logic or is there any another way to achieve this?

@VaguelySerious

Copy link
Copy Markdown
Member

@pranaygp Good point from @mpaicoding-byte, FatalError is a user-thrown error and should intentionally kill the workflow

@pranaygp

Copy link
Copy Markdown
Contributor

the thinking here was (we can revisit this) -

  • RetryableError (or any error) allows the workflow system to deterministically retry
  • FatalError bubbles into the LLM since the LLM was the invokeR of the function and the LLM needs to know the status of the tool call (success/failed state)

this maps to throwing errors in tools in AI sdk. when a tool throws an error it doesn't kill the entire generateText/streamText loop - it returns the failed tool call as an error response back to the LLM

@VaguelySerious

Copy link
Copy Markdown
Member

Ah, I remember. That too makes sense, though I can see how from the user's perspective, not being able to intentionally throw any error in a step (tool) that terminates the agent is counterintuitive

@mpaicoding-byte

Copy link
Copy Markdown

Thanks @VaguelySerious@pranaygp for looking at this. It could also be unintentional, maybe there is no try catch implemented properly and there was some fatal error from an api or some function, that ideally should be considered as a system error and the step should be retried and eventually the error should be propogated to workflow IMO

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mpaicoding-byte@VaguelySerious@pranaygp
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Handle FatalError in DurableAgent tool execution - #376

Merged
pranaygp merged 5 commits into
mainfrom
copilot/handle-tool-call-errors
Nov 20, 2025
Merged

Handle FatalError in DurableAgent tool execution#376
pranaygp merged 5 commits into
mainfrom
copilot/handle-tool-call-errors

Conversation

CopilotAI commented Nov 20, 2025

Copy link
Copy Markdown
Contributor

FatalError thrown in DurableAgent tool execution currently kills the workflow. It should convert to a tool error result sent back to the LLM, matching AI SDK behavior.

Changes

  • packages/ai/src/agent/durable-agent.ts: Wrap tool execution in try-catch. Convert FatalError to error-text tool result. Re-throw other errors for retry.
  • packages/ai/src/agent/durable-agent.test.ts: Add tests for error handling scenarios.

Example

constagent=newDurableAgent({model: 'anthropic/claude-opus',tools: {getTool: {description: 'Get data',inputSchema: z.object({id: z.string()}),execute: async({ id })=>{// Previously: kills workflow// Now: error sent to LLM as tool resultthrownewFatalError('Invalid ID');},},},});

The LLM receives the error as a tool result and can retry with different parameters or inform the user.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • fonts.googleapis.com
    • Triggering command: /usr/local/bin/node /usr/local/bin/node /home/REDACTED/work/workflow/workflow/node_modules/.pnpm/next@15.5.4_@opentelemetry&#43;api@1.9.0_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/jest-worker/processChild.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

View original Slack conversation


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@changeset-bot

changeset-botBot commented Nov 20, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6e61ab9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
@workflow/aiPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Nov 20, 2025

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentPreviewCommentsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreviewCommentNov 20, 2025 10:19pm
example-nextjs-workflow-webpackReadyReadyPreviewCommentNov 20, 2025 10:19pm
example-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-express-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-hono-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-nitro-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-nuxt-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-sveltekit-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-vite-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workflow-docsReadyReadyPreviewCommentNov 20, 2025 10:19pm

Convert FatalError exceptions in tool execution to tool-result errors that get
propagated back to the LLM, mimicking AI SDK behavior. Non-fatal errors are
re-thrown to allow workflow retry mechanisms to handle them.
- Updated executeTool to catch FatalError and convert to error-text tool result
- Added comprehensive tests for error handling scenarios
- All tests passing
Co-authored-by: pranaygp <1797812+pranaygp@users.noreply.github.com>
Remove unnecessary type assertion and add changeset for the DurableAgent fix
Co-authored-by: pranaygp <1797812+pranaygp@users.noreply.github.com>
Comment threadpackages/ai/src/agent/durable-agent.ts Outdated
@VaguelySerious
VaguelySerious marked this pull request as ready for review November 20, 2025 21:01
Comment thread.changeset/handle-tool-errors.md Outdated
@mpaicoding-byte

Copy link
Copy Markdown

Ideally it should stop the workflow right? It could be an issue with an api call in that step or some code logic in that step. So when that is fixed, then it should resume from that step completion. Wondering what was the intent behind this logic or is there any another way to achieve this?

@VaguelySerious

Copy link
Copy Markdown
Member

@pranaygp Good point from @mpaicoding-byte, FatalError is a user-thrown error and should intentionally kill the workflow

@pranaygp

Copy link
Copy Markdown
Contributor

the thinking here was (we can revisit this) -

  • RetryableError (or any error) allows the workflow system to deterministically retry
  • FatalError bubbles into the LLM since the LLM was the invokeR of the function and the LLM needs to know the status of the tool call (success/failed state)

this maps to throwing errors in tools in AI sdk. when a tool throws an error it doesn't kill the entire generateText/streamText loop - it returns the failed tool call as an error response back to the LLM

@VaguelySerious

Copy link
Copy Markdown
Member

Ah, I remember. That too makes sense, though I can see how from the user's perspective, not being able to intentionally throw any error in a step (tool) that terminates the agent is counterintuitive

@mpaicoding-byte

Copy link
Copy Markdown

Thanks @VaguelySerious@pranaygp for looking at this. It could also be unintentional, maybe there is no try catch implemented properly and there was some fatal error from an api or some function, that ideally should be considered as a system error and the step should be retried and eventually the error should be propogated to workflow IMO

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mpaicoding-byte@VaguelySerious@pranaygp
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Handle FatalError in DurableAgent tool execution - #376

Merged
pranaygp merged 5 commits into
mainfrom
copilot/handle-tool-call-errors
Nov 20, 2025
Merged

Handle FatalError in DurableAgent tool execution#376
pranaygp merged 5 commits into
mainfrom
copilot/handle-tool-call-errors

Conversation

CopilotAI commented Nov 20, 2025

Copy link
Copy Markdown
Contributor

FatalError thrown in DurableAgent tool execution currently kills the workflow. It should convert to a tool error result sent back to the LLM, matching AI SDK behavior.

Changes

  • packages/ai/src/agent/durable-agent.ts: Wrap tool execution in try-catch. Convert FatalError to error-text tool result. Re-throw other errors for retry.
  • packages/ai/src/agent/durable-agent.test.ts: Add tests for error handling scenarios.

Example

constagent=newDurableAgent({model: 'anthropic/claude-opus',tools: {getTool: {description: 'Get data',inputSchema: z.object({id: z.string()}),execute: async({ id })=>{// Previously: kills workflow// Now: error sent to LLM as tool resultthrownewFatalError('Invalid ID');},},},});

The LLM receives the error as a tool result and can retry with different parameters or inform the user.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • fonts.googleapis.com
    • Triggering command: /usr/local/bin/node /usr/local/bin/node /home/REDACTED/work/workflow/workflow/node_modules/.pnpm/next@15.5.4_@opentelemetry&#43;api@1.9.0_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/jest-worker/processChild.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

View original Slack conversation


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@changeset-bot

changeset-botBot commented Nov 20, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6e61ab9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
@workflow/aiPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Nov 20, 2025

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentPreviewCommentsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreviewCommentNov 20, 2025 10:19pm
example-nextjs-workflow-webpackReadyReadyPreviewCommentNov 20, 2025 10:19pm
example-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-express-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-hono-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-nitro-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-nuxt-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-sveltekit-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-vite-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workflow-docsReadyReadyPreviewCommentNov 20, 2025 10:19pm

Convert FatalError exceptions in tool execution to tool-result errors that get
propagated back to the LLM, mimicking AI SDK behavior. Non-fatal errors are
re-thrown to allow workflow retry mechanisms to handle them.
- Updated executeTool to catch FatalError and convert to error-text tool result
- Added comprehensive tests for error handling scenarios
- All tests passing
Co-authored-by: pranaygp <1797812+pranaygp@users.noreply.github.com>
Remove unnecessary type assertion and add changeset for the DurableAgent fix
Co-authored-by: pranaygp <1797812+pranaygp@users.noreply.github.com>
Comment threadpackages/ai/src/agent/durable-agent.ts Outdated
@VaguelySerious
VaguelySerious marked this pull request as ready for review November 20, 2025 21:01
Comment thread.changeset/handle-tool-errors.md Outdated
@mpaicoding-byte

Copy link
Copy Markdown

Ideally it should stop the workflow right? It could be an issue with an api call in that step or some code logic in that step. So when that is fixed, then it should resume from that step completion. Wondering what was the intent behind this logic or is there any another way to achieve this?

@VaguelySerious

Copy link
Copy Markdown
Member

@pranaygp Good point from @mpaicoding-byte, FatalError is a user-thrown error and should intentionally kill the workflow

@pranaygp

Copy link
Copy Markdown
Contributor

the thinking here was (we can revisit this) -

  • RetryableError (or any error) allows the workflow system to deterministically retry
  • FatalError bubbles into the LLM since the LLM was the invokeR of the function and the LLM needs to know the status of the tool call (success/failed state)

this maps to throwing errors in tools in AI sdk. when a tool throws an error it doesn't kill the entire generateText/streamText loop - it returns the failed tool call as an error response back to the LLM

@VaguelySerious

Copy link
Copy Markdown
Member

Ah, I remember. That too makes sense, though I can see how from the user's perspective, not being able to intentionally throw any error in a step (tool) that terminates the agent is counterintuitive

@mpaicoding-byte

Copy link
Copy Markdown

Thanks @VaguelySerious@pranaygp for looking at this. It could also be unintentional, maybe there is no try catch implemented properly and there was some fatal error from an api or some function, that ideally should be considered as a system error and the step should be retried and eventually the error should be propogated to workflow IMO

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mpaicoding-byte@VaguelySerious@pranaygp
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Handle FatalError in DurableAgent tool execution - #376

Merged
pranaygp merged 5 commits into
mainfrom
copilot/handle-tool-call-errors
Nov 20, 2025
Merged

Handle FatalError in DurableAgent tool execution#376
pranaygp merged 5 commits into
mainfrom
copilot/handle-tool-call-errors

Conversation

CopilotAI commented Nov 20, 2025

Copy link
Copy Markdown
Contributor

FatalError thrown in DurableAgent tool execution currently kills the workflow. It should convert to a tool error result sent back to the LLM, matching AI SDK behavior.

Changes

  • packages/ai/src/agent/durable-agent.ts: Wrap tool execution in try-catch. Convert FatalError to error-text tool result. Re-throw other errors for retry.
  • packages/ai/src/agent/durable-agent.test.ts: Add tests for error handling scenarios.

Example

constagent=newDurableAgent({model: 'anthropic/claude-opus',tools: {getTool: {description: 'Get data',inputSchema: z.object({id: z.string()}),execute: async({ id })=>{// Previously: kills workflow// Now: error sent to LLM as tool resultthrownewFatalError('Invalid ID');},},},});

The LLM receives the error as a tool result and can retry with different parameters or inform the user.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • fonts.googleapis.com
    • Triggering command: /usr/local/bin/node /usr/local/bin/node /home/REDACTED/work/workflow/workflow/node_modules/.pnpm/next@15.5.4_@opentelemetry&#43;api@1.9.0_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/jest-worker/processChild.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

View original Slack conversation


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@changeset-bot

changeset-botBot commented Nov 20, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6e61ab9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
@workflow/aiPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Nov 20, 2025

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentPreviewCommentsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreviewCommentNov 20, 2025 10:19pm
example-nextjs-workflow-webpackReadyReadyPreviewCommentNov 20, 2025 10:19pm
example-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-express-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-hono-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-nitro-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-nuxt-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-sveltekit-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workbench-vite-workflowReadyReadyPreviewCommentNov 20, 2025 10:19pm
workflow-docsReadyReadyPreviewCommentNov 20, 2025 10:19pm

Convert FatalError exceptions in tool execution to tool-result errors that get
propagated back to the LLM, mimicking AI SDK behavior. Non-fatal errors are
re-thrown to allow workflow retry mechanisms to handle them.
- Updated executeTool to catch FatalError and convert to error-text tool result
- Added comprehensive tests for error handling scenarios
- All tests passing
Co-authored-by: pranaygp <1797812+pranaygp@users.noreply.github.com>
Remove unnecessary type assertion and add changeset for the DurableAgent fix
Co-authored-by: pranaygp <1797812+pranaygp@users.noreply.github.com>
Comment threadpackages/ai/src/agent/durable-agent.ts Outdated
@VaguelySerious
VaguelySerious marked this pull request as ready for review November 20, 2025 21:01
Comment thread.changeset/handle-tool-errors.md Outdated
@mpaicoding-byte

Copy link
Copy Markdown

Ideally it should stop the workflow right? It could be an issue with an api call in that step or some code logic in that step. So when that is fixed, then it should resume from that step completion. Wondering what was the intent behind this logic or is there any another way to achieve this?

@VaguelySerious

Copy link
Copy Markdown
Member

@pranaygp Good point from @mpaicoding-byte, FatalError is a user-thrown error and should intentionally kill the workflow

@pranaygp

Copy link
Copy Markdown
Contributor

the thinking here was (we can revisit this) -

  • RetryableError (or any error) allows the workflow system to deterministically retry
  • FatalError bubbles into the LLM since the LLM was the invokeR of the function and the LLM needs to know the status of the tool call (success/failed state)

this maps to throwing errors in tools in AI sdk. when a tool throws an error it doesn't kill the entire generateText/streamText loop - it returns the failed tool call as an error response back to the LLM

@VaguelySerious

Copy link
Copy Markdown
Member

Ah, I remember. That too makes sense, though I can see how from the user's perspective, not being able to intentionally throw any error in a step (tool) that terminates the agent is counterintuitive

@mpaicoding-byte

Copy link
Copy Markdown

Thanks @VaguelySerious@pranaygp for looking at this. It could also be unintentional, maybe there is no try catch implemented properly and there was some fatal error from an api or some function, that ideally should be considered as a system error and the step should be retried and eventually the error should be propogated to workflow IMO

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mpaicoding-byte@VaguelySerious@pranaygp