{"record":{"id":"11d84d69079b5d7b","repo":"nodejs/node","slug":"optionname-must-contain-header-names","errorCode":null,"errorMessage":"${optionName} must contain header names","messagePattern":"(.+?) must contain header names","errorType":"exception","errorClass":"InvalidArgumentError","httpStatus":null,"severity":"error","filePath":"deps/undici/src/lib/handler/redirect-handler.js","lineNumber":191,"sourceCode":"    return name === 'authorization' || name === 'cookie' || name === 'proxy-authorization'\n  }\n  return false\n}\n\n// https://tools.ietf.org/html/rfc7231#section-6.4\nfunction normalizeStripHeaders (headers, optionName) {\n  if (headers == null) {\n    return null\n  }\n\n  if (!Array.isArray(headers)) {\n    throw new InvalidArgumentError(`${optionName} must be an array`)\n  }\n\n  const normalized = new Set()\n  for (const header of headers) {\n    if (typeof header !== 'string') {\n      throw new InvalidArgumentError(`${optionName} must contain header names`)\n    }\n\n    normalized.add(util.headerNameToString(header))\n  }\n  return normalized\n}\n\nfunction cleanRequestHeaders (headers, removeContent, unknownOrigin, stripHeaders, stripHeadersOnCrossOrigin) {\n  const ret = []\n  if (Array.isArray(headers)) {\n    for (let i = 0; i < headers.length; i += 2) {\n      if (!shouldRemoveHeader(headers[i], removeContent, unknownOrigin, stripHeaders, stripHeadersOnCrossOrigin)) {\n        ret.push(headers[i], headers[i + 1])\n      }\n    }\n  } else if (headers && typeof headers === 'object') {\n    const entries = util.hasSafeIterator(headers) ? headers : Object.entries(headers)\n","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/deps/undici/src/lib/handler/redirect-handler.js#L173-L209","documentation":"Thrown by normalizeStripHeaders when the stripHeadersOnRedirect / stripHeadersOnCrossOriginRedirect array contains an element that is not a string. Each entry must be a header-name string; numbers, booleans, objects, null, or undefined elements are rejected.","triggerScenarios":"Passing an array like ['authorization', 42] or ['x-trace', undefined] for stripHeadersOnRedirect / stripHeadersOnCrossOriginRedirect; building the array dynamically without filtering non-strings.","commonSituations":"Injecting a numeric or boolean config value into a header list; optional header names conditionally pushed as undefined; data sourced from JSON with mixed types.","solutions":["Ensure every element is a string; filter the source list: names.filter(n => typeof n === 'string').","Build the option only from strongly-typed string arrays at the configuration boundary.","Default to omitting the option when the filtered list is empty."],"exampleFix":"// before\nconst strip = ['authorization', traceId ?? undefined]\nnew Agent({ maxRedirections: 5, stripHeadersOnRedirect: strip })\n\n// after\nconst strip = ['authorization', traceId].filter(h => typeof h === 'string')\nnew Agent({ maxRedirections: 5, stripHeadersOnRedirect: strip.length ? strip : undefined })","handlingStrategy":"type-guard","validationCode":"function cleanStripHeaders(arr) {\n  return Array.isArray(arr) ? arr.filter(h => typeof h === 'string') : undefined\n}","typeGuard":"function isStringArray(v) { return Array.isArray(v) && v.every(x => typeof x === 'string') }","tryCatchPattern":"try { new Agent({ stripHeadersOnRedirect: arr }) } catch (e) { if (e.code === 'UND_ERR_INVALID_ARG') { new Agent({ stripHeadersOnRedirect: arr.filter(h => typeof h === 'string') }) } else throw e }","preventionTips":["Filter non-string entries before assigning header lists.","Type header arrays as string[] in TypeScript.","Avoid pushing optional values that may be undefined."],"tags":["undici","http","redirect","headers","configuration","validation"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}