Tell the LangGraph Bot's model a refused tool call was refused, not that it returned nothing - #518
Merged
davidmckayv merged 2 commits intoSep 13, 2026
Conversation
When /api/agent-tools/call would not run a call from the LangGraph Bot, it answered 401 or 403 with the reason under `error` and no `text`. callTool read that as a result and told the model "The tool returned nothing.", so the model told the person nothing was found. A response that is not a success is now a refusal, worded the way agent-langgraph-agui already words it, with the deployment's reason after it. The reading moves to its own module so it can be tested without binding a port. A successful answer is passed on unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
kevin9327
requested review from
MikeRyanDev,
davidmckayv,
guidovizoso,
mxmzb and
tylerslaton
as code owners
September 13, 2026 09:58
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
davidmckayv
approved these changes
Sep 13, 2026
davidmckayv
left a comment
Contributor
There was a problem hiding this comment.
Deep-reviewed clean (validation, no secret leak, fail-closed, agrees with existing layers). CI green.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What happens
A LangGraph Bot (
agent-langgraph) whose tool call the deployment will not run — it holds an agent token the deployment no longer accepts, or a token issued to a different Bot — asks a governed tool something:and answers:
The Drive has the files. The call never reached it.
Why
/api/agent-tools/callrefuses a callback it cannot verify before any grant is consulted (app.ts):callToolinagent-langgraph/src/index.tsreads every answer the same way, whatever its status:A refusal has no
text, so the model is handedThe tool returned nothing.and reports an absence.This is the exact failure #135 found and #136 addressed on the server side: that PR added the
mcp.callback_refusedaudit row so the trail stops agreeing nothing happened, and its CHANGELOG entry describes the Bot "returned nothing to its own model, and the model told the person there were no results". The trail was fixed; what the TypeScript Bot tells its model was not.The Python LangGraph Bot already handles this response.
agent-langgraph-agui/src/tool_runtime.py:The change
The reading of the response moves out of
index.tsintoagent-langgraph/src/tool-answer.ts, for the reasonhistory.ts,deltas.tsandstream.tsare their own modules:index.tscallsserve()at module scope, so a test cannot import it.Refused. Tool callback returned HTTP <status>., the sibling's wording, followed by the deployment's ownerrorwhen it sent one:Refused. Tool callback returned HTTP 401. Not authorised.Refused., the marker the transcript reads (app/src/lib/plugins/tool-result.ts), whichcallTool's other two refusals in the same function already start with. So the transcript draws it as a refusal rather than a result.callToolkeeps itstry/catch, so a network failure or an unreadable success body is still reported asThat tool could not be called: ….Where it runs
In the
agent-langgraphprocess, once per tool call, on the answer from the deployment.TOOL_CALL_RESULTevent; only its text changes for a refused call.Boundary and audit
mcp.callback_refusedfor this one.Changelog
A line at the top of
Unreleased, because a person asking this Bot now hears that the call was refused instead of that nothing was found.Proof
bun test tests/tool-answer.test.tsinagent-langgraph, with the tests applied andtool-answer.tsholdingcallTool's current reading moved verbatim (absolute paths shortened to the repository root, nothing else edited):The 401 and 403 failures are the bug. The third, a
502with an HTML body, is not a user-facing failure onmain: the parse error it throws is caught bycallTooland reported asThat tool could not be called: Failed to parse JSON. It is there to pin that the moved function answers a non-JSON failure without throwing.With the change: 5 pass, 0 fail.
Not a widening. The first two tests pass before and after. One pins that a successful result reaches the model as the deployment wrote it; the other pins that a refusal the store already made (a
200whosetextstarts withRefused.) is passed on untouched. So the only answers this changes are the ones that were not a success.Whole package (
bun testinagent-langgraph):mainagent-langgraphThe delta is exactly the five tests added.
bunx @biomejs/biome checkis clean ontool-answer.tsand its test. Onindex.tsit reports one import-order error, onimport { readReasoningEffort } from "./model-options"sitting above./model-key. That is onmaintoo (checked against an unmodified copy), and the one import this PR adds is already in order, so I have left that line alone.bunx prettier --checkis clean on both new files; onindex.tsandCHANGELOG.mdit reports the same style issues it reports onmain, none in lines added here.🤖 Generated with Claude Code