{"record":{"id":"a062feccd4a55c53","repo":"can1357/oh-my-pi","slug":"context-tools-must-be-an-array-when-present","errorCode":null,"errorMessage":"`context.tools` must be an array when present","messagePattern":"`context\\.tools` must be an array when present","errorType":"validation","errorClass":"AIError.ValidationError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/providers/pi-native-server.ts","lineNumber":131,"sourceCode":"\t} else if (typeof obj.model === \"object\" && obj.model !== null) {\n\t\tconst m = obj.model as Record<string, unknown>;\n\t\tif (typeof m.id === \"string\" && m.id.length > 0) modelId = m.id;\n\t}\n\tif (!modelId) throw new AIError.ValidationError(\"Missing `modelId` (or `model.id`) field\");\n\n\tconst context = obj.context;\n\tif (typeof context !== \"object\" || context === null || Array.isArray(context)) {\n\t\tthrow new AIError.ValidationError(\"Missing `context` object\");\n\t}\n\tconst ctxObj = context as Record<string, unknown>;\n\tif (!Array.isArray(ctxObj.messages)) {\n\t\tthrow new AIError.ValidationError(\"`context.messages` must be an array\");\n\t}\n\tif (ctxObj.systemPrompt !== undefined && !Array.isArray(ctxObj.systemPrompt)) {\n\t\tthrow new AIError.ValidationError(\"`context.systemPrompt` must be an array of strings when present\");\n\t}\n\tif (ctxObj.tools !== undefined && !Array.isArray(ctxObj.tools)) {\n\t\tthrow new AIError.ValidationError(\"`context.tools` must be an array when present\");\n\t}\n\n\tconst options: SimpleStreamOptions = {};\n\tconst rawOpts = obj.options;\n\tif (typeof rawOpts === \"object\" && rawOpts !== null && !Array.isArray(rawOpts)) {\n\t\tconst optsBag = options as Record<string, unknown>;\n\t\tfor (const [k, v] of Object.entries(rawOpts)) {\n\t\t\tif (v === undefined || v === null) continue;\n\t\t\tif (!ALLOWED_OPTION_KEYS.has(k as keyof SimpleStreamOptions)) continue;\n\t\t\toptsBag[k] = v;\n\t\t}\n\t}\n\n\t// `stream` defaults to true — pi-native clients overwhelmingly stream, and\n\t// matching `streamProxy`'s implicit-stream behavior avoids a one-flag papercut.\n\tconst stream = typeof obj.stream === \"boolean\" ? obj.stream : true;\n\n\treturn {","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/providers/pi-native-server.ts#L113-L149","documentation":"parseRequest accepts an optional `context.tools` array of tool definitions; when the key is present but is not an array (e.g. a single object, string, or null), this ValidationError is thrown. This keeps the tool schema well-formed before it reaches the streaming pipeline.","triggerScenarios":"Sending `context:{\"tools\":{...}}` (a single tool object) or `\"tools\":null` instead of an array of tool definitions.","commonSituations":"Clients registering one tool and passing it directly; Anthropic/OpenAI tool objects copied without wrapping in an array; JSON templates with tools accidentally set to null when no tools are wanted (omit the key instead).","solutions":["Wrap tool definitions in an array: `\"tools\":[{...}]`","Omit `tools` entirely rather than passing null when there are no tools","Validate each entry is a well-formed tool definition"],"exampleFix":"// before\n{\"context\":{\"messages\":[...],\"tools\":{\"name\":\"get_weather\"}}}\n// after\n{\"context\":{\"messages\":[...],\"tools\":[{\"name\":\"get_weather\"}]}}","handlingStrategy":"validation","validationCode":"const t = body?.context?.tools;\nif (t !== undefined && !Array.isArray(t)) throw new Error('context.tools must be an array when present');","typeGuard":"function hasToolsArray(ctx: { tools?: unknown }): ctx is { tools?: unknown[] } {\n  return ctx.tools === undefined || Array.isArray(ctx.tools);\n}","tryCatchPattern":"try { const req = parseRequest(body); } catch (e) { if (e instanceof AIError.ValidationError && String(e.message).includes('`context.tools`')) return respond400('context.tools must be an array when present'); throw e; }","preventionTips":["Always pass tools as arrays; wrap single definitions","Omit the key rather than sending null","Validate tools with a schema before sending"],"tags":["validation","request-schema","tools"],"backgroundTag":"schema-validation-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}