{"record":{"id":"97e8a7e8dcc3cab4","repo":"nodejs/node","slug":"optionname-must-be-an-array","errorCode":null,"errorMessage":"${optionName} must be an array","messagePattern":"(.+?) must be an array","errorType":"exception","errorClass":"InvalidArgumentError","httpStatus":null,"severity":"error","filePath":"deps/undici/src/lib/handler/redirect-handler.js","lineNumber":185,"sourceCode":"    return true\n  }\n  if (removeContent && name.startsWith('content-')) {\n    return true\n  }\n  if (unknownOrigin) {\n    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)) {","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/deps/undici/src/lib/handler/redirect-handler.js#L167-L203","documentation":"Thrown by normalizeStripHeaders when the stripHeadersOnRedirect or stripHeadersOnCrossOriginRedirect option is present but is not an array. These options let you list extra header names to drop when following a redirect (same-origin) or when the redirect crosses origins. Both must be arrays of header-name strings (or null/undefined to leave them unset).","triggerScenarios":"Passing stripHeadersOnRedirect / stripHeadersOnCrossOriginRedirect as a string (e.g. 'authorization'), a Set, an object, or a comma-separated list instead of an array, in Agent/Pool/Client opts or per-request opts.","commonSituations":"Migrating from a config format that used a single string or a Set; copying header-list config from another library that accepts different shapes; TypeScript where the type was loosened.","solutions":["Pass header names as an array of strings: ['authorization', 'x-api-key'].","If you have a Set or comma-separated string, convert first: Array.from(set) or str.split(',').","Omit the option when you only want the default Host/Content-* stripping behavior."],"exampleFix":"// before\nnew Agent({ maxRedirections: 5, stripHeadersOnRedirect: 'authorization,cookie' })\n\n// after\nnew Agent({ maxRedirections: 5, stripHeadersOnRedirect: ['authorization', 'cookie'] })","handlingStrategy":"validation","validationCode":"function normalizeStripHeaders(v) {\n  if (v == null) return undefined\n  if (!Array.isArray(v)) return [String(v)]\n  return v.filter(h => typeof h === 'string')\n}","typeGuard":"function isStripHeaders(v) { return v == null || (Array.isArray(v) && v.every(h => typeof h === 'string')) }","tryCatchPattern":"try { new Agent({ maxRedirections: 5, stripHeadersOnRedirect: list }) } catch (e) { if (e.code === 'UND_ERR_INVALID_ARG') { new Agent({ maxRedirections: 5, stripHeadersOnRedirect: Array.isArray(list) ? list : [list].filter(String) }) } else throw e }","preventionTips":["Always pass header lists as arrays of strings.","Convert Sets/comma-strings at the config boundary.","Omit the option to keep default Host/Content-* stripping."],"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"}