{"record":{"id":"6beacd15e23208a8","repo":"musistudio/claude-code-router","slug":"sample-must-be-a-json-object-with-an-object-body","errorCode":null,"errorMessage":"Sample must be a JSON object with an object body","messagePattern":"Sample must be a JSON object with an object body","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ui/src/pages/home/shared/routing.ts","lineNumber":178,"sourceCode":"  if (language && language !== \"javascript\" && language !== \"js\") return undefined;\n  const apiVersion = Number(value.apiVersion ?? value.version ?? ROUTER_SCRIPT_API_VERSION);\n  if (apiVersion !== ROUTER_SCRIPT_API_VERSION) return undefined;\n  const rawTimeout = Number(value.timeoutMs ?? value.timeout ?? ROUTER_SCRIPT_DEFAULT_TIMEOUT_MS);\n  const timeoutMs = Number.isFinite(rawTimeout)\n    ? Math.max(10, Math.min(ROUTER_SCRIPT_MAX_TIMEOUT_MS, Math.trunc(rawTimeout)))\n    : ROUTER_SCRIPT_DEFAULT_TIMEOUT_MS;\n  return {\n    apiVersion: ROUTER_SCRIPT_API_VERSION,\n    ...(file ? { file } : {}),\n    language: \"javascript\",\n    ...(source !== undefined ? { source } : {}),\n    timeoutMs\n  };\n}\n\nexport function normalizeRouteScriptSampleRequest(value: unknown): RouteScriptSampleRequest {\n  if (!isPlainRecord(value) || !isPlainRecord(value.body)) {\n    throw new Error(\"Sample must be a JSON object with an object body\");\n  }\n  const headers = normalizeRouteScriptSampleHeaders(value.headers);\n  return {\n    body: value.body,\n    headers,\n    ...(typeof value.method === \"string\" ? { method: value.method } : {}),\n    ...(typeof value.sessionId === \"string\" ? { sessionId: value.sessionId } : {}),\n    ...(typeof value.tokenCount === \"number\" ? { tokenCount: value.tokenCount } : {}),\n    ...(typeof value.url === \"string\" ? { url: value.url } : {})\n  };\n}\n\nfunction normalizeRouteScriptSampleHeaders(value: unknown): Record<string, string | string[]> {\n  if (value === undefined) return {};\n  if (!isPlainRecord(value)) {\n    throw new Error(\"Sample headers must be a JSON object containing string or string-array values\");\n  }\n  const headers: Record<string, string | string[]> = {};","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/ui/src/pages/home/shared/routing.ts#L160-L196","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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: {}, ... }"],"exampleFix":"// before\nrequest(JSON.stringify({ body: { a: 1 } }))\n\n// after\nrequest({ body: { a: 1 } })","handlingStrategy":"type-guard","validationCode":"if (typeof sample !== \"object\" || sample === null || Array.isArray(sample) || typeof sample.body !== \"object\" || sample.body === null || Array.isArray(sample.body)) { /* fix input before calling */ }","typeGuard":"function isRouteScriptSample(v: unknown): v is { body: Record<string, unknown>; headers?: unknown; method?: unknown } {\n  return typeof v === \"object\" && v !== null && !Array.isArray(v)\n    && typeof (v as any).body === \"object\" && (v as any).body !== null && !Array.isArray((v as any).body);\n}","tryCatchPattern":"try { normalizeRouteScriptSampleRequest(sample); } catch (e) { if (e instanceof Error && e.message.includes(\"JSON object with an object body\")) return badRequest(e.message); throw e; }","preventionTips":["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"],"tags":["validation","json","routing","schema"],"backgroundTag":"request-payload-validation-failed","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}