{"record":{"id":"97472e1eee0d2e41","repo":"denoland/deno","slug":"delay-cannot-be-greater-than-30-days-received-d","errorCode":null,"errorMessage":"Delay cannot be greater than 30 days: received ${delay}","messagePattern":"Delay cannot be greater than 30 days: received (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/kv/01_db.ts","lineNumber":71,"sourceCode":"const 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(\n      `expireIn must be a non-negative integer: received ${expireIn}`,\n    );\n  }","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/kv/01_db.ts#L53-L89","documentation":"The KV queue's maximum delay is capped at 30 days (`maxQueueDelay = 30*24*60*60*1000` ms). `kv.enqueue(..., { delay })` with a larger value throws before touching the database, because the queue's scheduling horizon does not extend further.","triggerScenarios":"`delay: 31*24*60*60*1000`; passing an absolute epoch timestamp such as `Date.now() + 60_000` (ms-since-epoch dwarfs the cap); miscalculated day-to-ms conversions (e.g. days multiplied by 1000 only).","commonSituations":"Long-horizon scheduled jobs (monthly reminders, expiring invitations); accidentally passing a timestamp instead of a duration; porting scheduler configs from systems that accept absolute dates.","solutions":["Pass a relative duration of at most 2_592_000_000 ms (30 days)","For longer horizons, enqueue a short-delay message that re-schedules itself when delivered, or store a due-date key and poll it","Double-check the value is not an absolute `Date.now()`-based timestamp"],"exampleFix":"// before\nawait kv.enqueue(job, { delay: Date.now() + 60_000 }); // absolute ms -> throws\n\n// after\nawait kv.enqueue(job, { delay: 60_000 }); // relative ms","handlingStrategy":"validation","validationCode":"const MAX_DELAY = 30 * 24 * 60 * 60 * 1000;\nif (delay > MAX_DELAY) throw new Error(\"delay > 30 days; use self-rescheduling\");\nawait kv.enqueue(msg, { delay });","typeGuard":"const isWithinQueueDelayCap = (d: number): boolean =>\n  Number.isFinite(d) && d >= 0 && d <= 30 * 24 * 60 * 60 * 1000;","tryCatchPattern":null,"preventionTips":["Assert the value is a relative duration, not Date.now()-based","Split horizons longer than 30 days into chained self-rescheduling messages","Unit-test enqueue option builders at the 30-day boundary"],"tags":["kv","deno-kv","queue","enqueue","validation"],"backgroundTag":"delay-out-of-range","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}