Skip to content

fix(scratchpad): stop large MCP outputs from producing spurious tool-call errors#263

Open
adrianfurlong wants to merge 2 commits into
mainfrom
20260619-scratchpad-footer-grep-fix
Open

fix(scratchpad): stop large MCP outputs from producing spurious tool-call errors#263
adrianfurlong wants to merge 2 commits into
mainfrom
20260619-scratchpad-footer-grep-fix

Conversation

@adrianfurlong

Copy link
Copy Markdown

Summary

Two recoverable-but-noisy ToolCallError warnings were appearing in production when the agent used MCP tools that return large outputs (e.g. Prometheus range queries):

  • Not JSON: file is not valid JSON
  • Invalid argument: Invalid regex: regex parse error … unclosed counted repetition

Both originate in the scratchpad layer, not the MCP servers. This PR fixes the root causes.

Root cause & fixes

1. Persistence footer corrupted the scratchpad file (wrapper.rs)

ComposedWrapper runs transform_output in reverse order, so the persistence wrapper runs before the scratchpad wrapper and appends an inline [Tool output saved to artifact: …] footer. The scratchpad then wrote that footer-bearing output verbatim to disk. For JSON tool results this left a non-JSON line after the closing brace, so the file was invalid JSON and get_in/schema/iterate_over failed with Not JSON.

Fix: strip the footer before counting, hashing, and writing. Passthrough returns keep the original output, so the footer still serves as the read_artifact pointer when the scratchpad doesn't intercept. Stripping before hashing also closes a dedup gap (identical raw outputs previously hashed differently due to call-specific footer text).

2. grep rejected literal patterns (tools.rs)

The model often passes a literal JSON fragment (e.g. "metric": {) as the grep pattern; the unescaped { fails to compile as a regex. grep now falls back to a literal (escaped) search instead of erroring.

3. Clearer non-JSON error (tools.rs)

The NotJson message now points the model at read/head/grep/slice so it self-corrects instead of retrying a JSON-only tool.

Tests

New regression tests (all passing; cargo clippy -p aura clean):

  • test_strip_artifact_footer
  • test_wrapper_strips_persistence_footer_before_write
  • test_grep_falls_back_to_literal_on_invalid_regex

Related

@adrianfurlong adrianfurlong requested a review from a team June 19, 2026 15:04
The persistence wrapper appends an inline
`[Tool output saved to artifact: ...]` footer to large tool outputs.
Because ComposedWrapper runs transform_output in reverse order,
persistence runs before the scratchpad wrapper, so the scratchpad
wrote the footer-bearing output verbatim to disk. For JSON tool
results (e.g. Prometheus range queries) this appended a non-JSON
line after the closing brace, making the file invalid JSON and
breaking get_in/schema/iterate_over with "Not JSON: file is not
valid JSON".

Strip the footer before counting, hashing, and writing. Passthrough
returns keep the original output so the footer still serves as the
read_artifact pointer when the scratchpad does not intercept.
Stripping before hashing also fixes a dedup gap where identical raw
outputs hashed differently due to call-specific footer text.
The LLM frequently passes a literal snippet (e.g. a JSON fragment
like `"metric": {`) as the grep pattern; the unescaped `{` fails to
compile as a regex ("unclosed counted repetition"), surfacing a
noisy Invalid regex tool-call error. Fall back to a literal
(escaped) search so grep behaves like the substring match the model
expected.

Also reword the NotJson error to point the model at
read/head/grep/slice for non-JSON files, so it self-corrects instead
of retrying a JSON-only tool.
@adrianfurlong adrianfurlong force-pushed the 20260619-scratchpad-footer-grep-fix branch from c4c279a to cbd2172 Compare June 19, 2026 15:18
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