{"record":{"id":"8d323558d4373bb1","repo":"nodejs/node","slug":"throwonmaxredirect-must-be-a-boolean","errorCode":null,"errorMessage":"throwOnMaxRedirect must be a boolean","messagePattern":"throwOnMaxRedirect must be a boolean","errorType":"exception","errorClass":"InvalidArgumentError","httpStatus":null,"severity":"error","filePath":"deps/undici/src/lib/handler/redirect-handler.js","lineNumber":27,"sourceCode":"const noop = () => {}\n\nclass RedirectHandler {\n  static buildDispatch (dispatcher, maxRedirections) {\n    if (maxRedirections != null && (!Number.isInteger(maxRedirections) || maxRedirections < 0)) {\n      throw new InvalidArgumentError('maxRedirections must be a positive number')\n    }\n\n    const dispatch = dispatcher.dispatch.bind(dispatcher)\n    return (opts, originalHandler) => dispatch(opts, new RedirectHandler(dispatch, maxRedirections, opts, originalHandler))\n  }\n\n  constructor (dispatch, maxRedirections, opts, handler) {\n    if (maxRedirections != null && (!Number.isInteger(maxRedirections) || maxRedirections < 0)) {\n      throw new InvalidArgumentError('maxRedirections must be a positive number')\n    }\n\n    if (opts.throwOnMaxRedirect != null && typeof opts.throwOnMaxRedirect !== 'boolean') {\n      throw new InvalidArgumentError('throwOnMaxRedirect must be a boolean')\n    }\n\n    this.dispatch = dispatch\n    this.location = null\n    const { maxRedirections: _, stripHeadersOnRedirect, stripHeadersOnCrossOriginRedirect, ...cleanOpts } = opts\n    this.opts = cleanOpts // opts must be a copy, exclude maxRedirections\n    this.opts.body = util.wrapRequestBody(this.opts.body)\n    this.stripHeadersOnRedirect = normalizeStripHeaders(stripHeadersOnRedirect, 'stripHeadersOnRedirect')\n    this.stripHeadersOnCrossOriginRedirect = normalizeStripHeaders(stripHeadersOnCrossOriginRedirect, 'stripHeadersOnCrossOriginRedirect')\n    this.maxRedirections = maxRedirections\n    this.handler = handler\n    this.history = []\n  }\n\n  onRequestStart (controller, context) {\n    this.handler.onRequestStart?.(controller, { ...context, history: this.history })\n  }\n","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/deps/undici/src/lib/handler/redirect-handler.js#L9-L45","documentation":"Thrown from the RedirectHandler constructor when opts.throwOnMaxRedirect is present but is not a boolean. throwOnMaxRedirect is a per-request flag that, when true, makes the handler throw instead of silently stopping once maxRedirections is exhausted. It must be a real boolean (or null/undefined to leave it unset); truthy/falsy values like 'true', 1, 0, or 'yes' are rejected because typeof is checked.","triggerScenarios":"Passing throwOnMaxRedirect as a string ('true'/'false'), a number (1/0), or any non-boolean truthy/falsy value in per-request dispatch opts or in the request options of an Agent configured with redirects.","commonSituations":"Flags parsed from query strings, headers, or JSON config arriving as strings; code that assumes 'throw on truthy' semantics; copying a value from a loosely-typed settings object.","solutions":["Coerce to a strict boolean before passing: throwOnMaxRedirect: Boolean(value) (or only set it when value is already boolean).","Omit the option entirely if you do not need throw-on-max behavior; it defaults to unset.","Validate incoming config with a boolean schema field (e.g. zod/ajv) at the trust boundary."],"exampleFix":"// before\nawait client.request({ path, method: 'GET', throwOnMaxRedirect: req.query.strict })\n\n// after\nconst strict = req.query.strict === 'true' || req.query.strict === true\nawait client.request({ path, method: 'GET', throwOnMaxRedirect: strict })","handlingStrategy":"type-guard","validationCode":"function strictBool(v) { return typeof v === 'boolean' ? v : undefined }","typeGuard":"function isBool(v) { return v == null || typeof v === 'boolean' }","tryCatchPattern":"try { await client.request({ throwOnMaxRedirect: flag }) } catch (e) { if (e.code === 'UND_ERR_INVALID_ARG') { await client.request({ /* drop throwOnMaxRedirect */ }) } else throw e }","preventionTips":["Coerce with Boolean() at the config boundary.","Reject 'true'/'false' strings from query params explicitly.","Omit the option when you do not need throw behavior."],"tags":["undici","http","redirect","configuration","validation","boolean"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}