nextauthjs/next-auth · error · TypeError
option maxAge is invalid: ${options.maxAge}
Error message
option maxAge is invalid: ${options.maxAge} What it means
The vendored cookie `serialize` requires options.maxAge, when defined, to be an integer number of seconds (which is what the Max-Age attribute supports). Non-integer values such as floats, NaN, or values derived from a Date are rejected with a TypeError.
Source
Thrown at packages/core/src/lib/vendored/cookie.ts:276
): string {
const enc = options?.encode || encodeURIComponent
if (!cookieNameRegExp.test(name)) {
throw new TypeError(`argument name is invalid: ${name}`)
}
const value = enc(val)
if (!cookieValueRegExp.test(value)) {
throw new TypeError(`argument val is invalid: ${val}`)
}
let str = name + "=" + value
if (!options) return str
if (options.maxAge !== undefined) {
if (!Number.isInteger(options.maxAge)) {
throw new TypeError(`option maxAge is invalid: ${options.maxAge}`)
}
str += "; Max-Age=" + options.maxAge
}
if (options.domain) {
if (!domainValueRegExp.test(options.domain)) {
throw new TypeError(`option domain is invalid: ${options.domain}`)
}
str += "; Domain=" + options.domain
}
if (options.path) {
if (!pathValueRegExp.test(options.path)) {
throw new TypeError(`option path is invalid: ${options.path}`)
}
View on GitHub (pinned to a1a16a5a77)
Solutions
- Pass maxAge as an integer number of SECONDS: `maxAge: 60 * 60 * 24` for one day.
- Convert from milliseconds with Math.floor(ms / 1000) before passing it.
- Guard the value: only set maxAge when Number.isInteger(maxAge), otherwise omit or fix it.
- Fix env/config parsing so the numeric value is valid (e.g. Number(process.env.X) with a fallback).
Example fix
// before
const maxAge = expiry.getTime() - Date.now() // milliseconds, non-integer
serialize("sid", token, { maxAge })
// after
const maxAge = Math.floor((expiry.getTime() - Date.now()) / 1000)
serialize("sid", token, { maxAge }) Defensive patterns
Strategy: validation
Validate before calling
if (maxAge !== undefined && !Number.isInteger(maxAge)) {
throw new Error(`maxAge must be an integer number of seconds, got: ${maxAge}`)
} Type guard
function isValidMaxAge(v: unknown): v is number {
return typeof v === "number" && Number.isInteger(v)
} Try / catch
try {
return serialize(name, value, { ...opts, maxAge })
} catch (e) {
if (e instanceof TypeError && e.message.includes("option maxAge is invalid")) {
return serialize(name, value, { ...opts, maxAge: Math.floor(Number(maxAge)) })
}
throw e
} Prevention
- Always express maxAge in SECONDS via integer arithmetic (e.g. 60 * 60 * 24)
- Convert Date math with Math.floor((expiryMs - Date.now()) / 1000)
- Validate env/config numbers with Number() plus an integer check before use
- Type option objects strictly (maxAge?: number) instead of any to catch Dates/strings at compile time
When it happens
Trigger: Calling serialize(name, val, { maxAge }) where maxAge is not an integer — e.g. a float like 3600.5, NaN from a failed parse, Infinity, or milliseconds from Date.now()/getTime() math.
Common situations: Passing milliseconds (Date.now diff) instead of seconds; computing expiry with non-integer division; reading maxAge from config/env where parseInt failed and produced NaN; TypeScript types loose enough (any) to let a Date or string slip in.
Related errors
- argument name is invalid: ${name}
- argument val is invalid: ${val}
- option domain is invalid: ${options.domain}
- option path is invalid: ${options.path}
- option expires is invalid: ${options.expires}
AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28).
Data as JSON: /api/errors/12fc2401c2754260.
Report an issue: GitHub.