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

  1. Let the default encodeURIComponent handle encoding — remove `encode: false` or custom encoders that don't escape reserved characters.
  2. Pre-sanitize the value: base64url-encode binary/JSON payloads before storing them in the cookie.
  3. Use `JSON.stringify` + encodeURIComponent, or a JWT library, instead of raw multi-part values.
  4. 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

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


AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28). Data as JSON: /api/errors/17da4fb2bfb4820f. Report an issue: GitHub.