Build1 publisher3 min readPublished
A bare ToolNode.invoke() has needed a Runtime object for eleven months
The langgraph-prebuilt 1.0.2 change that requires it shipped with no DeprecationWarning and no migration guide, and the same release flipped tool error handling off by default, which produces no traceback when it bites.
The Engineer · Build desk
What happened
- langgraph-prebuilt 1.0.2 added a required runtime parameter to ToolNode internals in October 2025, a change the dev.to postmortem calls necessary for LangGraph's new execution model.
- It arrived with no migration guide, no DeprecationWarning, and, per the postmortem, no correct pin in langgraph==1.0.1, so a clean pip install after October 29, 2025 picked up the new prebuilt.
- The same 1.0.x line reverted handle_tool_errors from True to False, so graphs that relied on the default lost tool error handling without a code change and without an exception.
- Three of the four original issues were still open at langgraph-prebuilt 1.0.8 in February 2026, and a September 2026 update names #6397 and #6486 as open against LangGraph 1.2.11.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- exposure The reported worst case is a green suite over a production agent whose tools swallow their own failures, which means CI signal and runtime behaviour diverged on a dependency bump nobody authored.
- constraint Tools that own sockets or file handles cannot guarantee cleanup on timeout while cancellation escapes ToolNode uncaught, and the only offered mitigation is per-tool defensive wrapping.
- cost Adopting the fixes touches code you did not plan to touch: every custom callable signature gains **kwargs, and every ToolNode construction gains an explicit error-handling flag.
- decision Any default that has already flipped once becomes a value you state at the call site, which turns handle_tool_errors from a preference into a reviewable rule.
Call `ToolNode.invoke()` outside a compiled graph and nothing injects the runtime context, so the node raises `ValueError: Missing required config key 'N/A' for 'tools'` [4]. Neither string in that message names the argument you now have to supply. A required parameter whose absence reports itself as config key `'N/A'` is not going to be found by grep. The repair is one import and one keyword: `Runtime` from `langgraph.runtime`, passed as `runtime=Runtime()` with no arguments, which gives the node its context without standing up a graph [5].
The async `TypeError` is the same change seen from the other side. LangGraph hands `runtime` as a keyword argument to every callable it invokes, so a custom coroutine whose signature does not accept it dies at the call [6]. That one is fixed in your signature, not at the call site: `**kwargs` absorbs `runtime` and whatever gets injected next [7]. Three of the four documented failure modes have a caller-side fix along those lines [2], but the cancellation path is the holdout: `asyncio.CancelledError` propagates through `ToolNode` uncaught when timeout logic cancels a task, your cleanup code never runs, and it shows up as flaky tests under load [10]. Until the upstream fix lands, the postmortem offers only defensive wrapping of tools that hold sockets or file handles in `try/except asyncio.CancelledError` [11].
Two instructions in the same writeup do not agree, and the disagreement is worth resolving before you copy either. The prose says pin the minor version, on the grounds that this project has a history of breaking changes on minor bumps [15]. The dependency block sets `langgraph = ">=1.0.2"` and `langgraph-prebuilt = ">=1.0.2"` with no upper bound and a comment that it is verified working through 1.2.11 [14]. The pin advice and the open-ended dependency block describe two different policies [3]. The floor is the defensible half, because it is what the `runtime=` and `handle_tool_errors=` fixes require. The missing ceiling is only safe if you are running the suite that would catch the next injected parameter, which is precisely the suite that broke here.
About eleven months separate the October 2025 change from the September 2026 check that the two issues are still open against 1.2.11 [1]. At that duration, `Runtime()` in a test is not a workaround waiting on a release; it is the entry point, and the postmortem says as much [13].
The material is silent on maintainer intent: nothing here states whether the `handle_tool_errors` default reverting to `False` was deliberate or a regression, and there is no fix timeline for the cancellation path. The scope of exposure is also narrower than the error list suggests: the `ValueError` fires only when the node is invoked outside a compiled graph [4], so a suite that exercises the compiled graph end to end never sees it and inherits the default flip alone, quietly [4].
What to watch
- Whether issue #6397 closes by making runtime optional on a bare invoke, or by documenting Runtime() as the supported test entry point.
- Whether a later release restores handle_tool_errors=True as the default, which changes nothing for code that passes the flag and everything for code that does not.
- An upstream fix for asyncio.CancelledError propagating through ToolNode, which is the one failure mode with no caller-side repair.