Skip to content

fix: handle double-serialized edits argument in edit_file tool#2144

Merged
dgageot merged 5 commits intodocker:mainfrom
trungutt:fix-edit-file-double-serialized
Mar 18, 2026
Merged

fix: handle double-serialized edits argument in edit_file tool#2144
dgageot merged 5 commits intodocker:mainfrom
trungutt:fix-edit-file-double-serialized

Conversation

@trungutt
Copy link
Copy Markdown
Contributor

@trungutt trungutt commented Mar 17, 2026

Summary

LLMs sometimes send the edits parameter of edit_file as a JSON string ("[{...}]") instead of a JSON array ([{...}]). This adds a custom UnmarshalJSON on EditFileArgs that detects this double-serialization and transparently unwraps it.

Since the ACP filesystem handler reuses builtin.EditFileArgs, this fix applies to both code paths.

Also validates that the path field is non-empty in the fallback branch, so a missing path produces a clear "path field is required" error instead of a confusing downstream failure.

Gordon added 2 commits March 17, 2026 12:22
LLMs sometimes send the edits parameter as a JSON string instead of
a JSON array (e.g. "[{...}]" rather than [{...}]). Add a custom
UnmarshalJSON on EditFileArgs that detects this and transparently
double-deserializes the string into the expected []Edit slice.

This also fixes the ACP filesystem handler since it reuses the
builtin.EditFileArgs type.
Add a check that the path field is non-empty when the
UnmarshalJSON fallback path is taken, so that a missing path
produces a clear error instead of a confusing downstream failure.
@trungutt trungutt marked this pull request as ready for review March 17, 2026 11:27
@trungutt trungutt requested a review from a team as a code owner March 17, 2026 11:27
Comment thread pkg/tools/builtin/filesystem.go Outdated
docker-agent[bot]

This comment was marked as resolved.

Gordon added 2 commits March 17, 2026 13:15
Remove the type alias approach (standard unmarshal + fallback) and
always parse through RawMessage. This simplifies the code and ensures
path validation happens consistently for both normal and
double-serialized edits.

Add test cases for missing/empty path with normal array edits.
The TUI renders tool calls while arguments are still streaming,
so it may unmarshal EditFileArgs with only a path and no edits
field. Accept nil/missing edits gracefully instead of returning
an error.

Also remove the strict path-is-required validation from
UnmarshalJSON since the handler already produces a clear error
when the path is empty. This keeps UnmarshalJSON focused on
parsing concerns.
@trungutt trungutt requested a review from rumpl March 17, 2026 12:27
Comment thread pkg/tools/builtin/filesystem_test.go Outdated
Check that each error case returns the expected error message,
not just that an error occurred.
@trungutt trungutt requested a review from rumpl March 17, 2026 13:27
@dgageot dgageot merged commit 55148f7 into docker:main Mar 18, 2026
4 checks passed
trungutt added a commit that referenced this pull request May 6, 2026
Open-weights models (DeepSeek, Qwen, GLM) repeat the same small set of
shape mistakes when calling tools whose schema includes an array field:
sending a JSON-stringified array, a bare scalar, or a single-key object
placeholder where the schema expects an array. Today every tool surface
that doesn't have a custom UnmarshalJSON fails strictly on these and the
model wastes a turn retrying with the same mistake.

This generalises the validate-then-repair design we already use for
edit_file (PRs #2452 and #2144) to every tool that goes through
tools.NewHandler. Strict json.Unmarshal still runs first; only when it
fails does the repair layer walk the destination type's fields and
attempt four narrow fixes at the exact paths the schema disagreed at.
Successful repairs emit a tool_input_repaired log entry tagged with the
tool name and the repairs applied so per-(model, tool) repair rates can
be tracked.
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.

3 participants