{"record":{"id":"515519dceae1e0f7","repo":"FlowiseAI/Flowise","slug":"invalid-header-key-value-must-be-a-string","errorCode":null,"errorMessage":"Invalid header \"${key}\": value must be a string","messagePattern":"Invalid header \"(.+?)\": value must be a string","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/components/src/headerValidation.ts","lineNumber":66,"sourceCode":"\n    for (const [key, value] of entries) {\n        if (typeof key !== 'string' || key.length === 0) {\n            throw new Error('Invalid header: key must be a non-empty string')\n        }\n        if (key.length > MAX_KEY_LENGTH) {\n            throw new Error(`Invalid header \"${key}\": key exceeds ${MAX_KEY_LENGTH} chars`)\n        }\n        if (!RFC7230_TOKEN.test(key)) {\n            throw new Error(`Invalid header \"${key}\": key contains illegal characters`)\n        }\n\n        const lower = key.toLowerCase()\n        if (DENIED_HEADER_NAMES.has(lower) || DENIED_HEADER_PREFIXES.some((p) => lower.startsWith(p))) {\n            throw new Error(`Invalid header \"${key}\": this header name is not allowed`)\n        }\n\n        if (typeof value !== 'string') {\n            throw new Error(`Invalid header \"${key}\": value must be a string`)\n        }\n        if (value.length > MAX_VALUE_LENGTH) {\n            throw new Error(`Invalid header \"${key}\": value exceeds ${MAX_VALUE_LENGTH} chars`)\n        }\n        for (let i = 0; i < value.length; i++) {\n            const code = value.charCodeAt(i)\n            if (code === 0x0d || code === 0x0a || (code < 0x20 && code !== 0x09)) {\n                throw new Error(`Invalid header \"${key}\": value contains illegal control characters`)\n            }\n        }\n    }\n}\n\n/**\n * Returns a copy of `headers` with credential-bearing entries (Authorization, Cookie, X-Api-Key, …)\n * replaced by a placeholder string. Used at trust boundaries before a header bag is exposed to flow\n * templates, observers, or logs. Comparison is case-insensitive; non-sensitive headers pass through.\n */","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/FlowiseAI/Flowise/blob/abe4a8601a058047b350c260676826e21dd14101/packages/components/src/headerValidation.ts#L48-L84","documentation":"Thrown by validateCustomHeaders() when a header value is not a JavaScript string. The validator enforces that every value in the headers bag is a string before it is forwarded to an outbound HTTP request, preventing accidental serialization of numbers, booleans, objects, or arrays into header lines. This is a type-enforcement guard, not a network-level check.","triggerScenarios":"Calling validateCustomHeaders() with a record where any value is a number (e.g. { 'X-Retry-Count': 3 }), a boolean, null, undefined, an array, or an object. The check at line 65 (typeof value !== 'string') fires before any length or character validation.","commonSituations":"Config objects built from typed application state where numeric fields leak into headers without String(). JSON parsed from external config that contains numeric values. Passing request options straight from a form or API response where numbers are common (retry counts, timeouts, content lengths).","solutions":["Coerce every header value to a string before calling validateCustomHeaders: build the headers object with template literals or String().","Audit the headers object at the call site and convert numeric/boolean values explicitly.","If the value can legitimately be non-string, drop the header or stringify it intentionally rather than letting it through raw."],"exampleFix":"// before\nvalidateCustomHeaders({ 'X-Retry-Count': retries, 'X-Verbose': true })\n\n// after\nvalidateCustomHeaders({ 'X-Retry-Count': String(retries), 'X-Verbose': String(true) })","handlingStrategy":"validation","validationCode":"// Pre-validate that every header value is a string before calling validateCustomHeaders\nfunction safeHeaders(input: Record<string, unknown>): Record<string, string> {\n  const out: Record<string, string> = {}\n  for (const [k, v] of Object.entries(input)) {\n    if (typeof v !== 'string') {\n      throw new Error(`Header '${k}' must be a string, got ${typeof v}`)\n    }\n    out[k] = v\n  }\n  return out\n}\n\nconst safe = safeHeaders(rawHeaders)\nvalidateCustomHeaders(safe)","typeGuard":"function isStringHeaders(h: Record<string, unknown>): h is Record<string, string> {\n  return Object.values(h).every((v) => typeof v === 'string')\n}","tryCatchPattern":"try {\n  validateCustomHeaders(headers)\n} catch (e) {\n  // surface a user-facing validation error, do not retry\n  throw new BadRequestError(String(e))\n}","preventionTips":["Always type header bags as Record<string, string> at API boundaries so non-string values fail to compile.","Coerce known-numeric fields with String() at the point of assignment.","Run validateCustomHeaders early in request building, before any network call."],"tags":["headers","type-validation","http","typescript"],"backgroundTag":null,"analyzedSha":"abe4a8601a058047b350c260676826e21dd14101","analyzedAt":"2026-08-12T16:04:40.823Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}