can1357/oh-my-pi · error · AIError.ValidationError

`context.systemPrompt` must be an array of strings when pres

Error message

`context.systemPrompt` must be an array of strings when present

What it means

parseRequest treats `context.systemPrompt` as optional, but when present it must be an array (of strings, per the message). Passing a bare string or any other non-array type raises this ValidationError so the server never receives a malformed system prompt.

Source

Thrown at packages/ai/src/providers/pi-native-server.ts:128

		modelId = obj.modelId;
	} else if (typeof obj.model === "string" && obj.model.length > 0) {
		modelId = obj.model;
	} 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.

View on GitHub (pinned to 9690622007)

Solutions

  1. Wrap the system prompt in an array, e.g. `"systemPrompt":["You are helpful"]`
  2. For multiple prompt blocks, pass each block as a separate array element
  3. Omit the field entirely if no system prompt is needed

Example fix

// before
{"context":{"messages":[...],"systemPrompt":"You are helpful"}}
// after
{"context":{"messages":[...],"systemPrompt":["You are helpful"]}}
Defensive patterns

Strategy: validation

Validate before calling

const sp = body?.context?.systemPrompt;
if (sp !== undefined && !(Array.isArray(sp) && sp.every(s => typeof s === 'string'))) throw new Error('context.systemPrompt must be string[] when present');

Type guard

function hasStringArraySystemPrompt(ctx: { systemPrompt?: unknown }): ctx is { systemPrompt?: string[] } {
  return ctx.systemPrompt === undefined || (Array.isArray(ctx.systemPrompt) && ctx.systemPrompt.every(s => typeof s === 'string'));
}

Try / catch

try { const req = parseRequest(body); } catch (e) { if (e instanceof AIError.ValidationError && String(e.message).includes('systemPrompt')) return respond400('context.systemPrompt must be an array of strings'); throw e; }

Prevention

When it happens

Trigger: Sending `context:{"systemPrompt":"You are helpful",...}` — a string instead of an array of strings.

Common situations: OpenAI-style clients used to `system` role strings; copying examples where systemPrompt was a single string; concatenating multiple prompt blocks into one string instead of array entries.

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/db9b69c53dab763a. Report an issue: GitHub.