musistudio/claude-code-router · error · Error
Sample must be a JSON object with an object body
Error message
Sample must be a JSON object with an object body
What it means
Thrown by normalizeRouteScriptSampleRequest when validating a route script sample request payload. The function requires the incoming value to be a plain JSON object that itself contains a `body` property which is also a plain object. Any non-object input (string, array, null, or missing/non-object body) is rejected before normalization proceeds.
Source
Thrown at packages/ui/src/pages/home/shared/routing.ts:178
if (language && language !== "javascript" && language !== "js") return undefined;
const apiVersion = Number(value.apiVersion ?? value.version ?? ROUTER_SCRIPT_API_VERSION);
if (apiVersion !== ROUTER_SCRIPT_API_VERSION) return undefined;
const rawTimeout = Number(value.timeoutMs ?? value.timeout ?? ROUTER_SCRIPT_DEFAULT_TIMEOUT_MS);
const timeoutMs = Number.isFinite(rawTimeout)
? Math.max(10, Math.min(ROUTER_SCRIPT_MAX_TIMEOUT_MS, Math.trunc(rawTimeout)))
: ROUTER_SCRIPT_DEFAULT_TIMEOUT_MS;
return {
apiVersion: ROUTER_SCRIPT_API_VERSION,
...(file ? { file } : {}),
language: "javascript",
...(source !== undefined ? { source } : {}),
timeoutMs
};
}
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[]> = {};View on GitHub (pinned to 99f24806c6)
Solutions
- Ensure the sample is a parsed object with an object body: { body: {...} }
- If the sample arrives as a string, JSON.parse it before calling the API
- For empty bodies, pass an explicit empty object: { body: {}, ... }
Example fix
// before
request(JSON.stringify({ body: { a: 1 } }))
// after
request({ body: { a: 1 } }) Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof sample !== "object" || sample === null || Array.isArray(sample) || typeof sample.body !== "object" || sample.body === null || Array.isArray(sample.body)) { /* fix input before calling */ } Type guard
function isRouteScriptSample(v: unknown): v is { body: Record<string, unknown>; headers?: unknown; method?: unknown } {
return typeof v === "object" && v !== null && !Array.isArray(v)
&& typeof (v as any).body === "object" && (v as any).body !== null && !Array.isArray((v as any).body);
} Try / catch
try { normalizeRouteScriptSampleRequest(sample); } catch (e) { if (e instanceof Error && e.message.includes("JSON object with an object body")) return badRequest(e.message); throw e; } Prevention
- Always JSON.parse raw sample strings before submission
- Default missing bodies to {} rather than omitting the key
- Validate with a type guard at the form/UI boundary
When it happens
Trigger: Calling request() (route script sampling) with a JSON string instead of a parsed object, an array, null, or an object whose `body` is missing, a string, a number, or an array.
Common situations: Passing JSON.stringify()'d sample data without JSON.parse, building the sample from a form where body was left as a raw text string, or sending an empty object {} because the request had no body configured.
Related errors
- Sample headers must be a JSON object containing string or st
- 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/6beacd15e23208a8.
Report an issue: GitHub.