denoland/deno · error · TypeError

Delay must be >= 0: received

Error message

Delay must be >= 0: received ${delay}

What it means

`Deno.Kv.enqueue(value, { delay })` schedules a message for future delivery; `validateQueueDelay` rejects a negative `delay` up front because a negative offset would target a time in the past, which the queue can never honor.

Solutions

  1. Clamp before the call: `delay = Math.max(0, delay)`
  2. Validate the computed delta and skip/log expired deadlines instead of enqueueing
  3. Confirm you pass a relative duration in milliseconds, not an absolute timestamp

Example fix

// before
const delay = deadline - Date.now(); // negative when deadline passed
await kv.enqueue(job, { delay });

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

Strategy: validation

Validate before calling

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

Type guard

const isValidQueueDelay = (d: unknown): d is number =>
  typeof d === "number" && Number.isFinite(d) && d >= 0;

Prevention

When it happens

Trigger: `kv.enqueue(msg, { delay: -100 })`; delays computed from timestamps that invert, e.g. `deadline - Date.now()` when the deadline has already passed by the time of the call.

Common situations: Deadline/reminder scheduling where the target time slipped into the past before enqueueing; unit confusion after converting seconds to ms and subtracting; porting cron-like logic that assumes clamping.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/55a94df4b0b77504. Report an issue: GitHub.

Appendix: source

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

const cloneableDeserializers = core.getCloneableDeserializers();

const encodeCursor: (
  selector: [Deno.KvKey | null, Deno.KvKey | null, Deno.KvKey | null],
  boundaryKey: Deno.KvKey,
) => string = (selector, boundaryKey) =>
  op_kv_encode_cursor(selector, boundaryKey);

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(

View on GitHub (pinned to 9ad36f7a2c)