fix(tools): coerce limit/offset in ai-sdk schemas#1202
Conversation
There was a problem hiding this comment.
Summary
Reviewed — found 1 issue(s). This change updates the AI SDK tool schemas in packages/tools/src/ai-sdk.ts to coerce limit and offset inputs to numbers. The review focused on validation behavior and found that z.coerce.number() broadens accepted inputs beyond numeric strings.
Findings
packages/tools/src/ai-sdk.ts
z.coerce.number()acceptsnulland empty strings as0, so optionallimitinputs can bypass defaults or validation and produce empty/invalid searches.
Verdict
null and empty strings are either treated as omitted before defaults apply or rejected.
| .describe(PARAMETER_DESCRIPTIONS.includeFullDocs), | ||
| limit: strict | ||
| ? z | ||
| ? z.coerce |
There was a problem hiding this comment.
z.coerce.number() applies Number(...) before .default(), so inputs like null or "" parse as 0 instead of using DEFAULT_VALUES.limit or being rejected. AI tool calls often emit optional fields as null; here that 0 can be passed to client.search.execute, causing empty or invalid searches. Consider preprocessing only numeric strings while mapping null/empty strings to undefined before the default applies, or rejecting them explicitly.
TestingTargeted schema/runtime validation and package-level regression checks were run by the testing subagent. Numeric-string coercion works for Commands run: bun /tmp/pr-1202-ai-sdk-coerce-check.ts
bunx vitest run src/tools.test.ts -t 'new tool operations'
bunx vitest run src/tools.test.ts -t 'client initialization'
bun run buildResult: Verdict
|
Fixes #1191
searchMemoriesanddocumentListtool calls fail withtool call validation failed: [/limit: expected number, but got string]when the LLM emits
limit/offsetas JSON strings (e.g."10").Changes all five numeric param schemas in
packages/tools/src/ai-sdk.tsfrom
z.number()toz.coerce.number():searchMemories.limit(strict + non-strict)documentList.limit(strict + non-strict)documentList.offsetBehavior
"10"→10; real numbers pass through unchangedundefinedstill triggers.default()/ staysundefinedfor optionaloffset"abc") are still rejected (NaN fails validation)toJSONSchemaand the AI SDK's schema conversion still emit"type": "number"with defaults intact, so the model-facing contractis identical
Known
z.coercetrade-off:""/nullcoerce to0instead of erroring.This is standard zod semantics and
documentListalready guards withlimit || DEFAULT_VALUES.limit.Testing
actual
inputSchemavia the AI SDK validation path — coerces correctlyin both strict and non-strict modes
main(52 passed; the 2failing files fail on
maintoo — one needsSUPERMEMORY_API_KEY,one has a pre-existing broken import)
tsc --noEmiterror count identical tomain; biome cleanNote: the OpenAI tools codepath (
src/openai/tools.ts) has a relatedgap (hand-written JSON schema, no coercion) — intentionally out of
scope; will be filed as a separate issue.
Session Details
(aside)to your comment to have me ignore it.