Skip to content

Recover repairable tool requests instead of discarding them - #1835

Open
revworxai wants to merge 1 commit into
agent0ai:readyfrom
revworxai:fix-misformat-final
Open

Recover repairable tool requests instead of discarding them#1835
revworxai wants to merge 1 commit into
agent0ai:readyfrom
revworxai:fix-misformat-final

Conversation

@revworxai

Copy link
Copy Markdown

Recover repairable tool requests instead of discarding them

Problem

"Message misformat, no valid tool request found" has been reported
continuously 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:

Cause Share
Model emitted native tool-call markup instead of the JSON envelope 37%
Model replied in plain markdown, no envelope at all 21%
Framework rejected JSON that had parsed successfully 24%
Empty completion from provider 14%
Malformed or truncated JSON 4%

What this PR changes

1. A process_tools extension 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:

  • Native tool-call markup in four vocabularies: <invoke>/<parameter>,
    <function>/<argument>, <function=name>, and <tool_call>{…}
  • Markdown fences, leaked reasoning tags, BOM and zero-width characters,
    surrounding prose
  • HTML <br> appearing where newlines should be inside the envelope
  • Single-element array wrappers, stringified tool_args, name/arguments
    alias 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:

  • Native markup: 100% recovered
  • Framework-rejected valid JSON: 87% recovered
  • Overall: 61% recovered

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 one
JSON root:

root = extract_json_root_string(content)
if root != content:
    return None

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 6b7302f6 the json_parse_dirty() path accepted
all 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_tools itself.

Suggested follow-up (filed separately)

  • Make the warning name its cause. This is cheap and would have collapsed 19
    months of scattered reports into separable bugs.
  • Check finish_reason for length rather than retrying truncated turns
    blindly.

Testing

  • Repair function exercised against 17 message shapes covering every failure
    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.
  • Replayed against 263 real warning events from saved chat histories.
  • Verified the extension self-disables cleanly on builds predating 6b7302f6,
    where extract_tool_request does not exist, rather than raising inside the
    agent loop.
  • Deployed on several v2.8 instances; no regressions observed over several days
    of normal use.

I have not run the full upstream test suite — happy to do so if you point me at
the expected command.

Sign up for free to 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.

1 participant