{"record":{"id":"74b9fc4d9b8d210a","repo":"ruvnet/ruflo","slug":"invalid-header-value","errorCode":"INVALID_HEADER_VALUE","errorMessage":"header \"${key}\" must be a string","messagePattern":"header \"(.+?)\" must be a string","errorType":"validation","errorClass":"HttpFetchValidationError","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-tools/http-fetch-tools.ts","lineNumber":116,"sourceCode":"  const out: Record<string, string> = {};\n  for (const [key, value] of Object.entries(headers)) {\n    const lower = key.toLowerCase();\n    if (!allowAuth) {\n      if ((FORBIDDEN_HEADERS_EXACT as readonly string[]).includes(lower)) {\n        throw new HttpFetchValidationError(\n          `header \"${key}\" is not allowed without CLAUDE_FLOW_HTTP_FETCH_ALLOW_AUTH=1`,\n          'FORBIDDEN_HEADER',\n        );\n      }\n      if (FORBIDDEN_HEADER_PREFIXES.some((p) => lower.startsWith(p))) {\n        throw new HttpFetchValidationError(\n          `header \"${key}\" is not allowed without CLAUDE_FLOW_HTTP_FETCH_ALLOW_AUTH=1`,\n          'FORBIDDEN_HEADER',\n        );\n      }\n    }\n    if (typeof value !== 'string') {\n      throw new HttpFetchValidationError(\n        `header \"${key}\" must be a string`,\n        'INVALID_HEADER_VALUE',\n      );\n    }\n    out[key] = value;\n  }\n  return out;\n}\n\nfunction clampNumber(raw: unknown, defaultValue: number, max: number): number {\n  if (raw === undefined || raw === null) return defaultValue;\n  const n = Number(raw);\n  if (!Number.isFinite(n) || n <= 0) return defaultValue;\n  return Math.min(Math.floor(n), max);\n}\n\nexport interface HttpFetchResult {\n  success: boolean;","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/mcp-tools/http-fetch-tools.ts#L98-L134","documentation":"Thrown by validateHeaders when a header value is not a string. HTTP headers must be string-typed for fetch; numbers, booleans, objects, or arrays are rejected to avoid silent coercion bugs and Node fetch type errors downstream. The error carries code INVALID_HEADER_VALUE and names the offending header key.","triggerScenarios":"Passing a headers object where any value is a number (e.g. timeout: 30), boolean, object, array, or null. The check runs after the forbidden-header gate, so a forbidden header with a non-string value throws FORBIDDEN_HEADER first.","commonSituations":"Passing a numeric retry/timeout as a header; a config object mistakenly nested in headers; a boolean flag; JSON objects intended as header metadata; Date objects.","solutions":["Stringify every header value: String(value) or template literals.","Move non-string metadata out of headers into the request body or a separate field.","Validate headers with an object-string-values type guard before calling http_fetch.","For JSON content, set 'content-type': 'application/json' and pass the JSON string in the body, not headers."],"exampleFix":"// before\nhttp_fetch({ url, headers: { 'x-retry': 3 } })\n// after\nhttp_fetch({ url, headers: { 'x-retry': String(3) } })","handlingStrategy":"type-guard","validationCode":"function stringifyHeaders(headers) {\n  const out = {};\n  for (const [k, v] of Object.entries(headers)) out[k] = typeof v === 'string' ? v : String(v);\n  return out;\n}","typeGuard":"function isStringHeaderRecord(h: unknown): h is Record<string, string> {\n  return typeof h === 'object' && h !== null && !Array.isArray(h)\n    && Object.values(h).every((v) => typeof v === 'string');\n}","tryCatchPattern":null,"preventionTips":["Type header objects as Record<string,string>.","Stringify numeric/boolean values at the call site.","Keep metadata out of headers."],"tags":["http-fetch","headers","validation","type-error","input-validation"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}