Recover repairable tool requests instead of discarding them - #1835
Open
revworxai wants to merge 1 commit into
Open
Recover repairable tool requests instead of discarding them#1835revworxai wants to merge 1 commit into
revworxai wants to merge 1 commit into
Conversation
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.
Recover repairable tool requests instead of discarding them
Problem
"Message misformat, no valid tool request found"has been reportedcontinuously since January 2025 — 36 issues, running at 5–6 per month through
2026. The reports never converge because one warning string covers at least
six distinct failure modes with mutually incompatible fixes. Reporter A has a
truncated response, reporter B has XML markup, reporter C has an empty
completion, and all three see identical text.
I classified 263 warning events from a production instance (v2.8, Claude Opus 5
via Anthropic API) by underlying cause:
What this PR changes
1. A
process_toolsextension that repairs recoverable messages.Runs before extraction, normalizes the message, re-emits canonical JSON. It is
a no-op when the message is already valid. It handles:
<invoke>/<parameter>,<function>/<argument>,<function=name>, and<tool_call>{…}surrounding prose
<br>appearing where newlines should be inside the envelopetool_args,name/argumentsalias shapes
It deliberately refuses to repair truncated JSON rather than fabricating a
closing brace, since that would invent tool arguments.
2. Prompt additions naming the failure explicitly.
Lists every forbidden markup vocabulary by name, states that response length is
not an exception to the format rule, and adds a pre-emit self-check.
Results
Replayed against the 263 historical events:
After deploying the prompt change, a follow-up measurement across new sessions
recorded zero new markup events. Subjectively, a session that had been
warning on nearly every turn produced none.
What this does NOT fix
Roughly half the failures are the model not using the envelope protocol at all
— answering in markdown, or returning nothing. No parser change reaches
those. They need either prompt-level or upstream work.
Notably, this occurred on a frontier model through a first-party API, not a
small local model. Issue #1699 currently attributes markup emission to weak
models; that framing looks incomplete. It may be worth discussing whether the
JSON-envelope-in-text protocol should remain the default for models with native
tool calling.
Related root cause
extract_tool_request()requires the entire stripped message to be exactly oneJSON root:
The parser finds and parses the envelope, then discards it because a character
sits outside the braces — a leading "Sure:", a trailing period, a markdown
fence, a BOM. Before commit
6b7302f6thejson_parse_dirty()path acceptedall of these.
A narrower alternative to this PR would be to fall back to the first
tool-shaped root on strict-match failure. I chose the extension approach
because it also covers the markup cases, but I am happy to reshape this if
maintainers prefer a change inside
extract_toolsitself.Suggested follow-up (filed separately)
months of scattered reports into separable bugs.
finish_reasonforlengthrather than retrying truncated turnsblindly.
Testing
mode found, plus negative controls confirming it does not rescue truncated
JSON, does not invent tool calls from prose, and does not alter already-valid
messages.
6b7302f6,where
extract_tool_requestdoes not exist, rather than raising inside theagent loop.
of normal use.
I have not run the full upstream test suite — happy to do so if you point me at
the expected command.