nextauthjs/next-auth · error · TypeError
argument val is invalid: ${val}
Error message
argument val is invalid: ${val} What it means
The vendored cookie `serialize` validates the ENCODED value against cookieValueRegExp. Cookie values may not contain spaces, commas, semicolons, backslashes, or non-ASCII characters. When the (encoded) value still contains forbidden characters, a TypeError is thrown instead of emitting a broken Set-Cookie header.
Source
Thrown at packages/core/src/lib/vendored/cookie.ts:268
*
* serialize('foo', 'bar', { httpOnly: true })
* => "foo=bar; httpOnly"
*/
export function serialize(
name: string,
val: string,
options?: SerializeOptions
): 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}`)
}
View on GitHub (pinned to a1a16a5a77)
Solutions
- Let the default encodeURIComponent handle encoding — remove `encode: false` or custom encoders that don't escape reserved characters.
- Pre-sanitize the value: base64url-encode binary/JSON payloads before storing them in the cookie.
- Use `JSON.stringify` + encodeURIComponent, or a JWT library, instead of raw multi-part values.
- Use multiple smaller cookies or a session store if the value legitimately contains complex data.
Example fix
// before
serialize("prefs", JSON.stringify(prefs), { encode: false })
// after
serialize("prefs", JSON.stringify(prefs)) // default encodeURIComponent Defensive patterns
Strategy: validation
Validate before calling
const COOKIE_VALUE_RE = /^[\u0021\u0023-\u005B\u005D-\u007E]*$/ const encoded = encodeURIComponent(value) if (!COOKIE_VALUE_RE.test(encoded)) throw new Error(`Value not cookie-safe even after encoding`) serialize(name, encoded, opts)
Type guard
function isCookieSafeValue(v: unknown): v is string {
return typeof v === "string" &&
/^[\u0021\u0023-\u005B\u005D-\u007E]*$/.test(encodeURIComponent(v))
} Try / catch
try {
return serialize(name, value, opts)
} catch (e) {
if (e instanceof TypeError && e.message.startsWith("argument val is invalid")) {
return serialize(name, Buffer.from(String(value)).toString("base64url"), opts)
}
throw e
} Prevention
- Never pass encode: false unless you are certain values are token-safe
- Base64url-encode JSON/binary payloads before storing them in cookies
- Prefer JWTs or server-side sessions over raw multi-part cookie values
- Keep a round-trip test: set the cookie, parse it back, assert equality
When it happens
Trigger: Calling serialize(name, val) where `val` — after passing through options?.encode || encodeURIComponent — still fails cookieValueRegExp, e.g. raw values with quotes/commas, or supplying `encode: false` / a custom encode that doesn't escape reserved characters.
Common situations: Storing raw JSON or user text in a cookie without encoding; passing `encode: false` for performance and hitting illegal characters; encoding non-Latin text with a custom encoder that leaves non-ASCII bytes; JWTs or tokens containing characters the chosen encoder doesn't escape.
Related errors
- argument name is invalid: ${name}
- option maxAge is invalid: ${options.maxAge}
- 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/17da4fb2bfb4820f.
Report an issue: GitHub.