{"record":{"id":"55a94df4b0b77504","repo":"denoland/deno","slug":"delay-must-be-0-received-delay","errorCode":null,"errorMessage":"Delay must be >= 0: received ${delay}","messagePattern":"Delay must be >= 0: received (.+?)","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/kv/01_db.ts","lineNumber":68,"sourceCode":"\nconst cloneableDeserializers = core.getCloneableDeserializers();\n\nconst encodeCursor: (\n  selector: [Deno.KvKey | null, Deno.KvKey | null, Deno.KvKey | null],\n  boundaryKey: Deno.KvKey,\n) => string = (selector, boundaryKey) =>\n  op_kv_encode_cursor(selector, boundaryKey);\n\nasync function openKv(path: string) {\n  const rid = await op_kv_database_open(path);\n  return new Kv(rid, kvSymbol);\n}\n\nconst maxQueueDelay = 30 * 24 * 60 * 60 * 1000;\n\nfunction validateQueueDelay(delay: number) {\n  if (delay < 0) {\n    throw new TypeError(`Delay must be >= 0: received ${delay}`);\n  }\n  if (delay > maxQueueDelay) {\n    throw new TypeError(\n      `Delay cannot be greater than 30 days: received ${delay}`,\n    );\n  }\n  if (NumberIsNaN(delay)) {\n    throw new TypeError(\"Delay cannot be NaN\");\n  }\n}\n\nfunction validateExpireIn(expireIn: number | undefined) {\n  if (expireIn === undefined) return;\n  // Reject NaN, Infinity, fractional and negative values. A non-finite\n  // expireIn otherwise reaches the native layer and overflows when computing\n  // the absolute expiry, panicking the process.\n  if (!NumberIsInteger(expireIn) || expireIn < 0) {\n    throw new TypeError(","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/kv/01_db.ts#L50-L86","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst delay = scheduledFor - Date.now(); // negative when past\nawait kv.enqueue(job, { delay });\n\n// after\nconst delay = Math.max(0, scheduledFor - Date.now());\nawait kv.enqueue(job, { delay });","handlingStrategy":"validation","validationCode":"function safeDelay(runAt: number, now = Date.now()): number { return Math.max(0, Math.floor(runAt - now)); }\nawait kv.enqueue(job, { delay: safeDelay(scheduledFor) });","typeGuard":"function isValidDelay(d: unknown): d is number { return typeof d === \"number\" && Number.isFinite(d) && d >= 0 && d <= 30 * 24 * 60 * 60 * 1000; }","tryCatchPattern":null,"preventionTips":["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."],"tags":["kv","queue","scheduling","validation"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}