denoland/deno · error · TypeError
Delay must be >= 0: received ${delay}
Error message
Delay must be >= 0: received ${delay} What it means
Deno.Kv.enqueue(msg, { delay }) validates the optional delivery delay before touching the database. A negative delay would schedule work in the past, so validateQueueDelay rejects any delay < 0 with a TypeError. delay is milliseconds until the message becomes deliverable.
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 89f33cbef2)
Solutions
- Clamp computed delays to 0: const delay = Math.max(0, runAt - Date.now()).
- Treat an already-past target as immediate execution (delay 0) or skip/reject the request explicitly.
- Pass a duration, not an absolute timestamp.
Example fix
// before
const delay = scheduledFor - Date.now(); // negative when past
await kv.enqueue(job, { delay });
// after
const delay = Math.max(0, scheduledFor - Date.now());
await kv.enqueue(job, { delay }); Defensive patterns
Strategy: validation
Validate before calling
function safeDelay(runAt: number, now = Date.now()): number { return Math.max(0, Math.floor(runAt - now)); }
await kv.enqueue(job, { delay: safeDelay(scheduledFor) }); Type guard
function isValidDelay(d: unknown): d is number { return typeof d === "number" && Number.isFinite(d) && d >= 0 && d <= 30 * 24 * 60 * 60 * 1000; } Prevention
- Always clamp schedule deltas with Math.max(0, ...).
- Treat already-past targets as immediate (delay 0) or reject them with your own error.
- Pass durations in ms, never epoch timestamps.
When it happens
Trigger: kv.enqueue(msg, { delay: -1000 }); computing delay as (targetTime - Date.now()) when the target is already past; Date.now() skew after clock changes; passing a timestamp instead of a duration (e.g. delay: 1735689600000 is caught by the 30-day cap instead); arithmetic on user input yielding -0-adjacent negatives.
Common situations: Scheduling jobs from user-supplied timestamps ('run at 3pm') where 3pm already passed; timezone math producing past dates; daylight-saving shifts; tests using fixed old clock values.
Related errors
- Delay cannot be greater than 30 days: received ${delay}
- Delay cannot be NaN
- Invalid backoffSchedule, interval at index ${i} is invalid
- Invalid cron schedule: start=${start}, end=${end}, every=${e
- Cannot create cron job, a unique name is required: received
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/55a94df4b0b77504.
Report an issue: GitHub.