Skip to content

feat(langchain): ToolErrorMiddleware - #38781

Merged
ccurme (ccurme) merged 4 commits into
masterfrom
cc/tool_error
Jul 14, 2026
Merged

ccurme (ccurme) merged 4 commits into
masterfrom
cc/tool_error

Conversation

@ccurme

@ccurme ccurme (ccurme) commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Resolves #37195

Adds a ToolErrorMiddleware that allows specification of exceptions to be caught and translated into ToolMessages.

Usage patterns below.

Basic usage: MyError becomes ToolMessage("Tool 'failing_tool' failed with MyError")

def on_error(exc: Exception, request: ToolCallRequest) -> str | None:
    if isinstance(exc, MyError):
        return f"Tool '{request.tool_call['name']}' failed with {type(exc).__name__}"
    # propagates everything else


agent = create_agent(
    model="...",
    tools=[failing_tool],
    middleware=[ToolErrorMiddleware(on_error)],
)

@github-actions github-actions Bot added feature For PRs that implement a new feature; NOT A FEATURE REQUEST internal langchain `langchain` package issues & PRs size: M 200-499 LOC labels Jul 10, 2026

@open-swe open-swe Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open SWE Review found 1 potential issue.

Open in WebView Open SWE trace

Comment thread libs/langchain_v1/langchain/agents/middleware/tool_error.py
@mdrxy Mason Daugherty (mdrxy) changed the title feat(langchain): add ToolErrorMiddleware feat(langchain): ToolErrorMiddleware Jul 10, 2026
Comment thread libs/langchain_v1/langchain/agents/middleware/tool_error.py Outdated
Comment thread libs/langchain_v1/langchain/agents/middleware/tool_error.py Outdated

@eyurtsev Eugene Yurtsev (eyurtsev) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

One more possibility is to just accept a single on_error callable and have the user responsible for all the logic... unless we want to provide some pre-built on_errors (like the code is doing now)

Main thing is to not invoke async code on the sync path

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API LGTM


def __init__(
self,
catch: Catch,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kwarg only?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed catch in favor of required (a)on_error, per discussion above.

Comment thread libs/langchain_v1/langchain/agents/middleware/tool_error.py Outdated

OnError = Callable[
[Exception, "ToolCallRequest"],
"str | list[str | dict[Any, Any]] | Awaitable[str | list[str | dict[Any, Any]]]",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not intelligible, what is this?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we just have it return a ToolMessage?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

str | list[str | dict[Any, Any]] is how content is typed on our messages :)

I've replaced this with str | list[ContentBlock] for simplicity and readability.

asking for a ToolMessage doesn't change the fact that users need to supply the content. they would just also need to supply tool_call_id, name, and status, which we can do for them.

@ccurme
ccurme (ccurme) merged commit ceb1e4e into master Jul 14, 2026
60 of 62 checks passed
@ccurme
ccurme (ccurme) deleted the cc/tool_error branch July 14, 2026 13:02
Hunter Lovell (hntrl) added a commit to langchain-ai/langchainjs that referenced this pull request Jul 14, 2026
## Summary

Ports `ToolErrorMiddleware` from
[langchain-ai/langchain#38781](langchain-ai/langchain#38781)
to LangChain.js. The new middleware lets agents selectively convert tool
execution failures into model-visible error messages while preserving
original exceptions by default.

## Changes

- Add and export `toolErrorMiddleware`, `ToolErrorHandler`, and
`ToolErrorMiddlewareConfig` from `langchain`.
- Support synchronous or asynchronous error handlers and structured
`ToolMessage` content.
- Allow handling to be restricted by tool name or tool instance.
- Propagate unhandled failures and LangGraph control-flow signals
unchanged, avoiding disclosure of raw exception details unless the
handler explicitly includes them.
- Add focused coverage for error handling, propagation, filtering,
structured content, disclosure control, and graph interrupts.
@eric-burel

Copy link
Copy Markdown

Hey folks, writing the initial issue was a long process of tinkering with the error APIs, so I am very thankful for this pull request and I hope this will increase LangChain agents overall quality with better error management :)

@ccurme

Copy link
Copy Markdown
Collaborator Author

Thanks for your work writing up the issue, Eric Burel (@eric-burel)!

The middleware is released, would appreciate any feedback you have. Docs: https://docs.langchain.com/oss/python/langchain/middleware/built-in#tool-error

Mason Daugherty (mdrxy) added a commit to langchain-ai/deepagents that referenced this pull request Aug 20, 2026
A model that passes malformed arguments to `ask_user` (for example an
empty questions list, or a choice with a blank value) previously crashed
the whole run: the tool raises `ValueError`, `ToolNode` never converted
it, and the error bubbled up as a fatal `Agent error`. The model never
saw the failure and got no chance to reissue corrected arguments, so the
user's turn dead-ended. Now those validation errors surface to the model
as an error `ToolMessage` it can read and fix in one retry, and the run
continues.

---

This wires langchain's `ToolErrorMiddleware` (available since
`langchain>=1.3.14`; this package already requires `>=1.3.15`) into the
agent's middleware stack, scoped to `ask_user` — the tool that validates
model-authored arguments by raising `ToolArgumentError`, a `ValueError`
subclass introduced here so recovery keys off intent rather than a
shared built-in type. (`read_file` is deliberately out of scope: it
already catches its own argument errors and returns an error
`ToolMessage`, and its remaining `ValueError`s are backend invariants
that must stay fatal.) The `on_error` handler returns a message naming
the tool and the validation detail (which the existing error text
already carries) for `ToolArgumentError`, and returns `None` for
everything else so unexpected errors still propagate and halt the run.
`ToolErrorMiddleware` re-raises LangGraph control-flow signals
(`GraphBubbleUp` / interrupts) unchanged, so `ask_user`'s
`interrupt()`-based flow is unaffected.

The tool itself still raises; only the run-level handling changes. This
is the approach decided externally and discussed in
langchain-ai/langchain#38781.
@eric-burel

Eric Burel (eric-burel) commented Sep 2, 2026

Copy link
Copy Markdown

Hi, thanks for this update, it definitely adresses my issue as is. I'd like to include it in my LangChain course, however currently the documentation section is empty. The docstring seems to properly explain how it works though.

Edit: actually something was wrong in my browser somehow and the section was hidden... sounds perfect thanks again!

@eric-burel

Copy link
Copy Markdown

Up, me again sorry I found out the issue: the middleware is documented in Python, but not yet in TypeScript, that's why I saw it with your link but not when navigating through the documentation which was configured in TypeScript:
image

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

Labels

feature For PRs that implement a new feature; NOT A FEATURE REQUEST internal langchain `langchain` package issues & PRs size: M 200-499 LOC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@wrap_tool_call middleware could allow to catch exceptions and turn them into ToolException

4 participants