{"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":"validation","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/89f33cbef296a2b287f323d42de54c871fa69c77/ext/kv/01_db.ts#L53-L89","documentation":"validateQueueDelay also enforces an upper bound: kv.enqueue's delay may not exceed 30 days (maxQueueDelay = 30*24*60*60*1000 ms). Longer deferrals are rejected because the queue's scheduler does not support month-scale delays.","triggerScenarios":"kv.enqueue(msg, { delay: 45 * 24 * 60 * 60 * 1000 }); passing an absolute epoch timestamp as delay (seconds or ms); computing delay from a far-future calendar date; 'remind me in a year' features implemented directly on enqueue.","commonSituations":"Timestamp-vs-duration confusion (delay: Date.now() + week); long-term reminders or expirations; config-driven retry horizons larger than intended; copy from systems that allow arbitrary TTLs.","solutions":["Cap the delay at 30 days or schedule for <= 30 days out.","For longer deferrals, enqueue a 'check' message that re-enqueues with another delay when the time hasn't come yet (chained enqueue).","If you passed a timestamp by mistake, subtract now: delay = runAt - Date.now(), then clamp to [0, 30 days]."],"exampleFix":"// before\nawait kv.enqueue({ type: \"renew\" }, { delay: runAtMs }); // runAtMs ~ epoch -> huge\n\n// after\nconst delay = Math.min(Math.max(0, runAtMs - Date.now()), 30 * 24 * 60 * 60 * 1000);\nawait kv.enqueue({ type: \"renew\" }, { delay });","handlingStrategy":"validation","validationCode":"const MAX_DELAY = 30 * 24 * 60 * 60 * 1000;\nconst delay = Math.min(Math.max(0, runAt - Date.now()), MAX_DELAY);\nawait kv.enqueue(job, { delay });","typeGuard":"function isEnqueueDelay(d: unknown): d is number { return typeof d === \"number\" && Number.isFinite(d) && d >= 0 && d <= 30 * 24 * 60 * 60 * 1000; }","tryCatchPattern":null,"preventionTips":["Cap delays at 30 days at the call site.","For longer waits, chain enqueues via a re-check message.","Beware timestamp-vs-duration mixups: always subtract Date.now()."],"tags":["kv","queue","scheduling","limits"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}