Skip to content

fix(core): return bad tool params to the model instead of ending the run - #57

Merged
K-Mistele merged 1 commit into
mainfrom
fix/tool-param-errors-to-model
Oct 8, 2026
Merged

K-Mistele merged 1 commit into
mainfrom
fix/tool-param-errors-to-model

Conversation

@K-Mistele

Copy link
Copy Markdown
Contributor

Problem

When the model sends a tool call whose params don't match the tool's schema (for example a missing required key), the whole run ends with an error. The model should get a failed tool result instead, so it can fix the call and carry on.

This happened live in effect-channels: the agent called update_plan without title and the run ended with Missing key at ["title"].

Cause

Fold calls languageModel.streamText({ ..., disableToolCallResolution: true }) (AgentRuntimeLayer.ts). In that mode, streamText decodes each tool call's params against tool.setParameters(Schema.toEncoded(tool.parametersSchema)) while it reads the reply (effect/src/ai/LanguageModel.ts:1542, 2424). A bad call fails the reply, so Fold writes an error entry and ends the run.

Fold's own toolkit.handle already checks params when it runs the tool, and turns a bad call into a failed tool result. It never got the chance.

Fix

The tools handed to streamText accept any params while the reply is read. Each one is a copy of the tool whose setParameters returns tool.setParameters(Schema.Unknown). That's the same thing streamText does when it runs the tools itself.

Nothing else on the tool changes. Providers build the JSON schema from the unchanged parametersSchema, so OpenAI and Anthropic still apply their schema transforms, and the model sees the same schema as before.

Why not use Tool.dynamic with a precomputed JSON schema? Providers skip their transforms for dynamic tools, which changes the schema OpenAI and Anthropic models see.

Tests

AgentRuntimeToolParams.vi.test.ts: the model calls echo with { txet: 'hi' }. The run completes, the tool result is a ToolParameterValidationError reading Missing key at ["text"], and the model is still offered the real echo schema. The test fails without the fix (outcome error).

bun run test in fold-core: 332 passed. Typecheck, oxlint, oxfmt and typed-lint are clean.

🤖 Generated with Claude Code

With tool resolution disabled, streamText decodes each tool call's params
against the tool's schema while it reads the reply, so one bad call failed
the reply and ended the run. The tools handed to streamText now accept any
params there; Fold's toolkit.handle checks them when it runs the tool and
returns a failed tool result the model can fix. Providers still build the
same JSON schema, since only setParameters changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HumanLayer-Session: https://app.dev.codelayer.gg/sessions/01a1196a-ea93-79e6-9500-a2651678f1ea
@humanlayer-channels-app humanlayer-channels-app Bot added the bug Something isn't working label Oct 8, 2026
@K-Mistele
K-Mistele merged commit e3a8aeb into main Oct 8, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant