{"record":{"id":"4bd9037eb54216b4","repo":"can1357/oh-my-pi","slug":"validation-failed-for-tool-toolcall-name-too","errorCode":null,"errorMessage":"Validation failed for tool \"${toolCall.name}\": Tool call arguments are not valid JSON.\\nParse Error: ${parseError}\\nRaw JSON:\\n${truncatedRawJson}","messagePattern":"Validation failed for tool \"(.+?)\": Tool call arguments are not valid JSON\\.\\\\nParse Error: (.+?)\\\\nRaw JSON:\\\\n(.+?)","errorType":"validation","errorClass":"AIError.ValidationError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/utils/validation.ts","lineNumber":1940,"sourceCode":"}\n\n/**\n * Validates tool call arguments against an ArkType or plain JSON Schema schema.\n * Applies conservative LLM-quirk normalization before declaring failure.\n *\n * @throws Error with a formatted message when validation cannot be reconciled.\n */\nexport function validateToolArguments(tool: Tool, toolCall: ToolCall): ToolCall[\"arguments\"] {\n\tconst originalArgs = toolCall.arguments;\n\tif (originalArgs && typeof originalArgs === \"object\" && \"__parseError\" in originalArgs) {\n\t\tconst parseError = originalArgs.__parseError;\n\t\tconst rawJson = String(originalArgs.__rawJson ?? \"\");\n\t\tconst maxLen = 512;\n\t\tconst truncatedRawJson =\n\t\t\trawJson.length <= maxLen\n\t\t\t\t? rawJson\n\t\t\t\t: `${rawJson.slice(0, maxLen)}… [truncated ${rawJson.length - maxLen} chars]`;\n\t\tthrow new AIError.ValidationError(\n\t\t\t`Validation failed for tool \"${toolCall.name}\": Tool call arguments are not valid JSON.\\nParse Error: ${parseError}\\nRaw JSON:\\n${truncatedRawJson}`,\n\t\t);\n\t}\n\tconst ctx = getValidationContext(tool);\n\tconst { json } = ctx;\n\n\t// Always normalize first — strip null/string \"null\" from optional fields,\n\t// strip optional empty strings only when their property schema rejects the\n\t// explicit value, and substitute defaults. Handles LLM outputting\n\t// placeholders for \"no value\" even when validation would otherwise pass.\n\tlet normalizedArgs: unknown = originalArgs;\n\tlet changed = false;\n\n\t// Unwrap accidentally double-JSON-encoded object keys before any schema\n\t// pass. LLMs sometimes emit `{ \"\\\"op\\\"\": \"done\" }`, so the property name\n\t// arrives quote-wrapped; left alone it reads as an unrecognized key, gets\n\t// dropped by the coercion repair, and re-surfaces as a missing-required\n\t// error. Running first means every later pass sees the corrected names.","sourceCodeStart":1922,"sourceCodeEnd":1958,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/utils/validation.ts#L1922-L1958","documentation":"When a tool call's arguments carry a __rawJson field (raw unparsed JSON from the provider), validateToolCall attempts JSON.parse; on failure it throws AIError.ValidationError embedding the tool name, the parser's error, and up to 512 chars of the raw JSON (truncated with an ellipsis marker). This means the model produced malformed JSON arguments that could not be handed to the tool.","triggerScenarios":"Provider streams tool-call arguments as raw JSON fragments that fail to parse (truncated stream, invalid JSON syntax like single quotes/trailing commas, non-UTF8 artifacts); validateToolCall/validateToolArguments receives originalArgs containing __rawJson whose parse throws.","commonSituations":"Model output cut off by max_tokens leaving truncated JSON; a weak/quantized local model emitting invalid JSON; reasoning models wrapping JSON in prose or code fences; streaming interruptions mid tool-call.","solutions":["Inspect the Raw JSON section of the error to identify the malformed portion (truncation vs syntax)","Increase max output tokens / fix stream interruption so arguments complete","Retry the request — malformed generation is often transient","If using a lenient model, add a repair pass (e.g. jsonrepair) on __rawJson before validation, or catch the error and ask the model to re-emit the arguments","Catch AIError.ValidationError in the tool-execution loop and surface it back to the model as a corrective message"],"exampleFix":"// before — crash on malformed args\nconst args = validateToolCall(tools, toolCall);\n// after — recover by feeding the error back to the model\ntry {\n  const args = validateToolCall(tools, toolCall);\n} catch (err) {\n  if (err instanceof AIError.ValidationError) {\n    messages.push({ role: \"user\", content: `Your arguments for ${toolCall.name} were invalid: ${err.message}. Please re-emit valid JSON.` });\n  } else throw err;\n}","handlingStrategy":"try-catch","validationCode":"const raw = (toolCall.arguments as any)?.__rawJson;\nif (typeof raw === \"string\") {\n  try { JSON.parse(raw); } catch (e) {\n    // malformed upstream JSON — repair or fail fast before validation\n    toolCall.arguments = JSON.parse(jsonRepair(raw));\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  const args = validateToolCall(tools, toolCall);\n} catch (err) {\n  if (err instanceof AIError.ValidationError && /not valid JSON/.test(err.message)) {\n    // retry the generation or feed the parse error back to the model\n  } else throw err;\n}","preventionTips":["Set adequate max_tokens so streamed JSON completes","Catch and surface parse errors back to the model for self-correction","Prefer providers/models known to emit valid JSON tool arguments","Log truncated raw JSON from the error for diagnostics"],"tags":["json","tool-calling","validation","llm","streaming"],"backgroundTag":"invalid-tool-arguments-json","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}