vercel/ai · error · TypeError

Continuation maxAgeMs must be a positive integer.

Error message

Continuation maxAgeMs must be a positive integer.

What it means

resolveCodeModeContinuationSecurity validates the maxAgeMs option and throws a TypeError unless it is a positive, finite integer. maxAgeMs determines how long a signed continuation token remains valid (expiresAtMs = issuedAtMs + maxAgeMs). Values like 0, negative numbers, non-integers, NaN, or Infinity are rejected.

Source

Thrown at packages/code-mode/src/continuation-capability.ts:52

  options: CodeModeContinuationSecurityOptions = {},
): ResolvedCodeModeContinuationSecurity {
  const signingKey =
    options.signingKey === undefined
      ? Buffer.from(defaultSigningKey)
      : typeof options.signingKey === 'string'
        ? Buffer.from(options.signingKey)
        : Buffer.from(options.signingKey);
  if (signingKey.byteLength === 0) {
    throw new TypeError('Continuation signing key must not be empty.');
  }

  const maxAgeMs = options.maxAgeMs ?? defaultMaxAgeMs;
  if (
    !Number.isInteger(maxAgeMs) ||
    !Number.isFinite(maxAgeMs) ||
    maxAgeMs <= 0
  ) {
    throw new TypeError('Continuation maxAgeMs must be a positive integer.');
  }

  return { signingKey, maxAgeMs };
}

export function signCodeModeContinuation(
  continuation: UnsignedCodeModeContinuation,
  security = resolveCodeModeContinuationSecurity(),
): CodeModeContinuation {
  const issuedAtMs = Date.now();
  const auth: Omit<CodeModeContinuationAuth, 'signature'> = {
    alg: SIGNATURE_ALGORITHM,
    nonce: randomBytes(16).toString('hex'),
    issuedAtMs,
    expiresAtMs: issuedAtMs + security.maxAgeMs,
  };
  return {
    ...structuredClone(continuation),

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Pass maxAgeMs as a positive integer in milliseconds, e.g. 60 * 60 * 1000 for 1 hour
  2. If unsure, omit the option to use the library default (1 hour)
  3. Validate the value before passing: Number.isInteger(v) && Number.isFinite(v) && v > 0
  4. Fix the upstream parse producing NaN/0 (e.g. guard Number(config.maxAgeMs) || DEFAULT)

Example fix

// before
setCodeModeContinuationSigningKey(key, { maxAgeMs: Number(process.env.MAX_AGE) }); // NaN
// after
const maxAgeMs = Number.parseInt(process.env.MAX_AGE ?? '', 10);
setCodeModeContinuationSigningKey(key, {
  maxAgeMs: Number.isInteger(maxAgeMs) && maxAgeMs > 0 ? maxAgeMs : 60 * 60 * 1000,
});
Defensive patterns

Strategy: validation

Validate before calling

const maxAgeMs = options.maxAgeMs;
if (maxAgeMs !== undefined && (!Number.isInteger(maxAgeMs) || !Number.isFinite(maxAgeMs) || maxAgeMs <= 0)) {
  throw new Error(`maxAgeMs must be a positive integer (ms), got: ${String(maxAgeMs)}`);
}

Type guard

function isValidMaxAgeMs(v: unknown): v is number {
  return typeof v === 'number' && Number.isInteger(v) && Number.isFinite(v) && v > 0;
}

Try / catch

try {
  resolveCodeModeContinuationSecurity({ signingKey: key, maxAgeMs });
} catch (error) {
  if (error instanceof TypeError && /maxAgeMs/.test(error.message)) {
    console.error('Bad maxAgeMs value:', maxAgeMs);
    return resolveCodeModeContinuationSecurity({ signingKey: key }); // use library default
  }
  throw error;
}

Prevention

When it happens

Trigger: Passing maxAgeMs: 0 or a negative value to setCodeModeContinuationSigningKey or a security options object; passing NaN/Infinity from a bad numeric parse (Number('abc'), Number.POSITIVE_INFINITY); passing a float like 1500.5; passing a value computed as 0 due to a multiplication by an unset config variable.

Common situations: Config value parsed with parseInt from an empty string yielding NaN; template-driven config where maxAgeMs is a string that got Number()'d to NaN; a default of 0 used for 'unset'; mistaking seconds for milliseconds and passing sub-1ms fractional values.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/8bf036ae4c304855. Report an issue: GitHub.