denoland/deno · error · TypeError

Delay cannot be NaN

Error message

Delay cannot be NaN

What it means

The final check in validateQueueDelay: delay must not be NaN. NaN arises from parsing garbage ('parseInt("soon")'), arithmetic on undefined (undefined - Date.now()), or JSON payloads with null coerced oddly. Note the checks are ordered: NaN passes the < 0 and > max comparisons as false and is only caught by the explicit NumberIsNaN test.

Source

Thrown at ext/kv/01_db.ts:76

async function openKv(path: string) {
  const rid = await op_kv_database_open(path);
  return new Kv(rid, kvSymbol);
}

const maxQueueDelay = 30 * 24 * 60 * 60 * 1000;

function validateQueueDelay(delay: number) {
  if (delay < 0) {
    throw new TypeError(`Delay must be >= 0: received ${delay}`);
  }
  if (delay > maxQueueDelay) {
    throw new TypeError(
      `Delay cannot be greater than 30 days: received ${delay}`,
    );
  }
  if (NumberIsNaN(delay)) {
    throw new TypeError("Delay cannot be NaN");
  }
}

function validateExpireIn(expireIn: number | undefined) {
  if (expireIn === undefined) return;
  // Reject NaN, Infinity, fractional and negative values. A non-finite
  // expireIn otherwise reaches the native layer and overflows when computing
  // the absolute expiry, panicking the process.
  if (!NumberIsInteger(expireIn) || expireIn < 0) {
    throw new TypeError(
      `expireIn must be a non-negative integer: received ${expireIn}`,
    );
  }
}

const maxQueueBackoffIntervals = 5;
const maxQueueBackoffInterval = 60 * 60 * 1000;

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Validate/coerce before calling: const d = Number(delay); if (!Number.isFinite(d)) throw new Error('bad delay').
  2. Default missing values explicitly: const delay = opts.delay ?? 0.
  3. Strip units from config values or parse strictly (Number('500') not parseInt('500ms')).

Example fix

// before
const delay = config.waitMs - Date.now(); // config.waitMs undefined -> NaN
await kv.enqueue(job, { delay });

// after
const delay = Math.max(0, (config.waitMs ?? Date.now()) - Date.now());
await kv.enqueue(job, { delay });
Defensive patterns

Strategy: validation

Validate before calling

const delay = Number(opts.delay ?? 0);
if (!Number.isFinite(delay)) throw new Error(`invalid delay: ${opts.delay}`);
await kv.enqueue(job, { delay });

Type guard

function isFiniteNonNegative(n: unknown): n is number { return typeof n === "number" && Number.isFinite(n) && n >= 0; }

Prevention

When it happens

Trigger: kv.enqueue(msg, { delay: undefined - Date.now() }); delay: Number.parseInt(process.env.DELAY_MS, 10) with a non-numeric env var; delay: NaN from JSON.parse of a missing field defaulted incorrectly; delay: +"1e3 ms" parse failures.

Common situations: Optional config omitted then used in arithmetic; environment variables with units ('500ms') fed to parseInt; schema-less input payloads; defaulting with || where 0 was intended.

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/5accd80f38700bbf. Report an issue: GitHub.