{"record":{"id":"2714374541b69a3f","repo":"can1357/oh-my-pi","slug":"validation-failed-for-tool-toolcall-name-n","errorCode":null,"errorMessage":"Validation failed for tool \"${toolCall.name}\":\\n${errors}\\n\\nReceived arguments:\\n${JSON.stringify(receivedArgs, null, 2)}","messagePattern":"Validation failed for tool \"(.+?)\":\\\\n(.+?)\\\\n\\\\nReceived arguments:\\\\n(.+?)","errorType":"validation","errorClass":"AIError.ValidationError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/utils/validation.ts","lineNumber":2064,"sourceCode":"\t// Format validation errors nicely. The header phrase is asserted by\n\t// existing tests; the detailed body is informational.\n\tconst errors = result.messages.join(\"\\n\") || \"Unknown validation error\";\n\n\t// Truncate long per-field strings: the full payload (potentially hundreds\n\t// of KB for write/edit-class calls) would otherwise round-trip back to the\n\t// model inside the tool error.\n\tconst receivedArgs = changed\n\t\t? {\n\t\t\t\toriginal: truncateArgsForError(originalArgs),\n\t\t\t\tnormalized: truncateArgsForError(normalizedArgs),\n\t\t\t}\n\t\t: truncateArgsForError(originalArgs);\n\n\tconst errorMessage = `Validation failed for tool \"${\n\t\ttoolCall.name\n\t}\":\\n${errors}\\n\\nReceived arguments:\\n${JSON.stringify(receivedArgs, null, 2)}`;\n\n\tthrow new AIError.ValidationError(errorMessage);\n}\n\n/**\n * Runs up to {@link MAX_COERCION_PASSES} issue-driven coercion rounds,\n * re-applying the schema normalizations after each round because a coercion\n * may unwrap JSON-string containers and expose fields the pre-validation\n * passes could not reach.\n */\nfunction runCoercionPasses(\n\tctx: ValidationContext,\n\targs: unknown,\n\tinitial: ContextValidationResult,\n): { args: unknown; result: ContextValidationResult; changed: boolean } {\n\tconst { json } = ctx;\n\tlet normalizedArgs = args;\n\tlet result = initial;\n\tlet changed = false;\n\tfor (let pass = 0; pass < MAX_COERCION_PASSES; pass += 1) {","sourceCodeStart":2046,"sourceCodeEnd":2082,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/utils/validation.ts#L2046-L2082","documentation":"Thrown as AIError.ValidationError when a tool call's arguments fail zod/JSON-schema validation in the AI layer. The message includes the list of validation issues, plus the (possibly truncated) received arguments, so the developer can see exactly which fields were wrong. This is the library's contract check that LLM-emitted tool arguments match the tool's declared parameter schema before execution.","triggerScenarios":"Calling agent/LLM completion with tool calling where the model emits arguments that don't satisfy the tool's schema: missing required fields, wrong types, extra unknown fields rejected by the schema, or JSON strings that can't be coerced after MAX_COERCION_PASSES rounds in validateToolCall.","commonSituations":"Weaker/smaller models hallucinating or omitting required parameters; schema changes in a tool after a model was prompted with a stale tool description; the model double-encoding JSON as strings; strict schemas (no coercion possible) fed to models prone to typo'd enum values.","solutions":["Inspect the 'Received arguments' section of the message against the tool's schema to identify the mismatched field.","Loosen the tool schema (make fields optional, use unions/z.coerce) so valid-but-odd model output passes.","Improve the tool description and parameter descriptions so the model emits correct arguments.","Retry the request with a stronger model or ask the model to repair its tool arguments."],"exampleFix":"// before: strict schema rejects string numbers from the model\nconst schema = z.object({ count: z.number() });\n// after: coerce common LLM output shapes\nconst schema = z.object({ count: z.coerce.number() });","handlingStrategy":"try-catch","validationCode":"// pre-validate arguments before sending to the model loop\nconst parsed = tool.schema.safeParse(args);\nif (!parsed.success) {\n  console.warn(\"args will fail validation:\", parsed.error.issues);\n}","typeGuard":"function hasRequiredFields(args: Record<string, unknown>, req: readonly string[]): args is Record<string, unknown> & { [k: string]: unknown } {\n  return req.every(k => k in args && args[k] !== undefined);\n}","tryCatchPattern":"import { AIError } from \"@oh-my-pi/pi-ai\";\ntry {\n  await agent.step();\n} catch (err) {\n  if (err instanceof AIError.ValidationError) {\n    // err.message lists issues + received args; ask the model to repair\n    await agent.retryWithCorrection(err.message);\n  } else throw err;\n}","preventionTips":["Write precise zod descriptions/enums so the model sees the expected shapes.","Prefer z.coerce for numeric/boolean fields LLMs often stringify.","Use stronger models for strict-schema tools.","Catch AIError.ValidationError and feed the issue list back for a repair round."],"tags":["validation","tool-calling","schema","llm"],"backgroundTag":"tool-argument-schema-validation-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}