{"record":{"id":"e9123eb251c2b6d4","repo":"denoland/deno","slug":"expirein-must-be-a-non-negative-integer-received","errorCode":null,"errorMessage":"expireIn must be a non-negative integer: received ${expireIn}","messagePattern":"expireIn must be a non-negative integer: received (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/kv/01_db.ts","lineNumber":86,"sourceCode":"    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  }\n}\n\nconst maxQueueBackoffIntervals = 5;\nconst maxQueueBackoffInterval = 60 * 60 * 1000;\n\nfunction validateBackoffSchedule(backoffSchedule: number[]) {\n  if (backoffSchedule.length > maxQueueBackoffIntervals) {\n    throw new TypeError(\n      `Invalid backoffSchedule, max ${maxQueueBackoffIntervals} intervals allowed`,\n    );\n  }\n  for (let i = 0; i < backoffSchedule.length; ++i) {\n    const interval = backoffSchedule[i];\n    if (\n      interval < 0 || interval > maxQueueBackoffInterval ||","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/kv/01_db.ts#L68-L104","documentation":"`kv.set(key, value, { expireIn })` takes a relative TTL in whole milliseconds. `validateExpireIn` rejects anything that is not a non-negative integer — NaN, Infinity, fractional, and negative values — because such input reaches the native layer, overflows the absolute-expiry computation, and would panic the whole process (per the comment in the source).","triggerScenarios":"`expireIn: 1.5` (fractional ms), `expireIn: -1000`, `expireIn: Infinity`, or NaN from failed parsing; second-to-ms conversions producing floats (`ttlSeconds * 1000.0` on fractional input).","commonSituations":"TTLs derived from user input or config parsing; mixing seconds and milliseconds; math on undefined values passed straight into `kv.set`.","solutions":["Round and clamp: `Math.max(0, Math.round(expireIn))`","Validate `Number.isInteger(expireIn) && expireIn >= 0` before the write","Omit the option entirely when no expiration is intended (undefined is accepted)"],"exampleFix":"// before\nawait kv.set(\"session\", data, { expireIn: ttlSeconds * 1.5 }); // fractional -> throws\n\n// after\nawait kv.set(\"session\", data, { expireIn: Math.max(0, Math.round(ttlSeconds * 1000)) });","handlingStrategy":"validation","validationCode":"if (expireIn !== undefined && (!Number.isInteger(expireIn) || expireIn < 0)) {\n  throw new Error(`invalid expireIn: ${expireIn}`);\n}\nawait kv.set(key, value, { expireIn });","typeGuard":"const isValidExpireIn = (v: unknown): v is number | undefined =>\n  v === undefined || (typeof v === \"number\" && Number.isInteger(v) && v >= 0);","tryCatchPattern":null,"preventionTips":["Normalize TTLs: Math.max(0, Math.round(ms)) at the boundary","Type the option as `expireIn?: number` and validate integers once in a wrapper around kv.set","Convert all external TTL units to integer milliseconds before use"],"tags":["kv","deno-kv","ttl","expirein","validation"],"backgroundTag":"invalid-ttl","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"}