musistudio/claude-code-router · error · Error
Sample headers must be a JSON object containing string or st
Error message
Sample headers must be a JSON object containing string or string-array values
What it means
Thrown by normalizeRouteScriptSampleHeaders while validating the `headers` field of a route script sample request. When headers are provided they must be a plain JSON object; anything else (array, string, null, number) fails normalization with this error.
Source
Thrown at packages/ui/src/pages/home/shared/routing.ts:194
export function normalizeRouteScriptSampleRequest(value: unknown): RouteScriptSampleRequest {
if (!isPlainRecord(value) || !isPlainRecord(value.body)) {
throw new Error("Sample must be a JSON object with an object body");
}
const headers = normalizeRouteScriptSampleHeaders(value.headers);
return {
body: value.body,
headers,
...(typeof value.method === "string" ? { method: value.method } : {}),
...(typeof value.sessionId === "string" ? { sessionId: value.sessionId } : {}),
...(typeof value.tokenCount === "number" ? { tokenCount: value.tokenCount } : {}),
...(typeof value.url === "string" ? { url: value.url } : {})
};
}
function normalizeRouteScriptSampleHeaders(value: unknown): Record<string, string | string[]> {
if (value === undefined) return {};
if (!isPlainRecord(value)) {
throw new Error("Sample headers must be a JSON object containing string or string-array values");
}
const headers: Record<string, string | string[]> = {};
for (const [name, headerValue] of Object.entries(value)) {
if (typeof headerValue === "string") {
headers[name] = headerValue;
continue;
}
if (Array.isArray(headerValue) && headerValue.every((entry) => typeof entry === "string")) {
headers[name] = headerValue;
continue;
}
throw new Error("Sample headers must be a JSON object containing string or string-array values");
}
return headers;
}
export function normalizeRouterRuleCondition(value: unknown): RouterRuleCondition | undefined {
if (!isPlainRecord(value)) {View on GitHub (pinned to 99f24806c6)
Solutions
- Provide headers as a plain object of string or string[] values, e.g. { "Content-Type": "application/json" }
- Omit the headers field entirely (undefined returns {})
- Set null-valued headers to undefined or remove them
Example fix
// before
{ body: {}, headers: "Accept: application/json" }
// after
{ body: {}, headers: { "Accept": "application/json" } } Defensive patterns
Strategy: type-guard
Validate before calling
const h = sample.headers;
if (h !== undefined && (typeof h !== "object" || h === null || Array.isArray(h))) { /* fix or drop headers */ } Type guard
function isHeadersObject(v: unknown): v is Record<string, string | string[]> {
return typeof v === "object" && v !== null && !Array.isArray(v);
} Try / catch
try { normalizeRouteScriptSampleHeaders(headers); } catch (e) { if (e instanceof Error && e.message.includes("Sample headers")) { headers = {}; } else throw e; } Prevention
- Build headers with a parser (e.g. Object.fromEntries) instead of pasting strings
- Omit headers entirely when there are none
- Never set headers to null; use undefined
When it happens
Trigger: Passing headers as a string (e.g. "Content-Type: application/json"), an array of [name, value] pairs, null, or a URLSearchParams object instead of a plain object keyed by header name.
Common situations: Copy-pasting a curl-style header string instead of splitting it into an object; serializing headers before submission; leaving headers explicitly set to null rather than undefined.
Related errors
- Sample must be a JSON object with an object body
- Unsupported CCR link target.
- Provider manifest must be a JSON object.
- Provider payload must be a JSON object.
- Chrome login import payload must be an object.
AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27).
Data as JSON: /api/errors/453591c236bf3315.
Report an issue: GitHub.