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

  1. Provide headers as a plain object of string or string[] values, e.g. { "Content-Type": "application/json" }
  2. Omit the headers field entirely (undefined returns {})
  3. 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

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


AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27). Data as JSON: /api/errors/453591c236bf3315. Report an issue: GitHub.