can1357/oh-my-pi · error · AIError.ValidationError
`context.tools` must be an array when present
Error message
`context.tools` must be an array when present
What it means
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.
Source
Thrown at packages/ai/src/providers/pi-native-server.ts:131
} else if (typeof obj.model === "object" && obj.model !== null) {
const m = obj.model as Record<string, unknown>;
if (typeof m.id === "string" && m.id.length > 0) modelId = m.id;
}
if (!modelId) throw new AIError.ValidationError("Missing `modelId` (or `model.id`) field");
const context = obj.context;
if (typeof context !== "object" || context === null || Array.isArray(context)) {
throw new AIError.ValidationError("Missing `context` object");
}
const ctxObj = context as Record<string, unknown>;
if (!Array.isArray(ctxObj.messages)) {
throw new AIError.ValidationError("`context.messages` must be an array");
}
if (ctxObj.systemPrompt !== undefined && !Array.isArray(ctxObj.systemPrompt)) {
throw new AIError.ValidationError("`context.systemPrompt` must be an array of strings when present");
}
if (ctxObj.tools !== undefined && !Array.isArray(ctxObj.tools)) {
throw new AIError.ValidationError("`context.tools` must be an array when present");
}
const options: SimpleStreamOptions = {};
const rawOpts = obj.options;
if (typeof rawOpts === "object" && rawOpts !== null && !Array.isArray(rawOpts)) {
const optsBag = options as Record<string, unknown>;
for (const [k, v] of Object.entries(rawOpts)) {
if (v === undefined || v === null) continue;
if (!ALLOWED_OPTION_KEYS.has(k as keyof SimpleStreamOptions)) continue;
optsBag[k] = v;
}
}
// `stream` defaults to true — pi-native clients overwhelmingly stream, and
// matching `streamProxy`'s implicit-stream behavior avoids a one-flag papercut.
const stream = typeof obj.stream === "boolean" ? obj.stream : true;
return {View on GitHub (pinned to 9690622007)
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
Example fix
// before
{"context":{"messages":[...],"tools":{"name":"get_weather"}}}
// after
{"context":{"messages":[...],"tools":[{"name":"get_weather"}]}} Defensive patterns
Strategy: validation
Validate before calling
const t = body?.context?.tools;
if (t !== undefined && !Array.isArray(t)) throw new Error('context.tools must be an array when present'); Type guard
function hasToolsArray(ctx: { tools?: unknown }): ctx is { tools?: unknown[] } {
return ctx.tools === undefined || Array.isArray(ctx.tools);
} Try / catch
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; } Prevention
- Always pass tools as arrays; wrap single definitions
- Omit the key rather than sending null
- Validate tools with a schema before sending
When it happens
Trigger: Sending `context:{"tools":{...}}` (a single tool object) or `"tools":null` instead of an array of tool definitions.
Common situations: 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).
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- cachedContent cannot be combined with request-level ${incomp
- Missing `context` object
- `context.messages` must be an array
- `context.systemPrompt` must be an array of strings when pres
- Tool "${toolCall.name}" not found
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/a062feccd4a55c53.
Report an issue: GitHub.