{"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":"validation","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/89f33cbef296a2b287f323d42de54c871fa69c77/ext/kv/01_db.ts#L68-L104","documentation":"Deno.Kv expireIn (used by kv.set/get with { expireIn } and in atomic checks/mutations) must be a non-negative integer number of milliseconds. The guard rejects NaN, Infinity, fractional values, and negatives because a non-finite or fractional value would otherwise reach the native layer and overflow/panic while computing the absolute expiry timestamp.","triggerScenarios":"kv.set(key, val, { expireIn: 1.5 }) fractional; { expireIn: -1000 }; { expireIn: NaN } from arithmetic; { expireIn: Infinity } or { expireIn: 24 * 60 * 60 * 1000 * 365 } overflowing past safe integers is fine numerically but Infinity from division by 0 is not; expireIn: '3600' as a string coerces to NaN under NumberIsInteger.","commonSituations":"TTL configs in seconds passed directly as ms (or vice versa producing fractions after division); expirations computed from user input; JSON strings not converted to numbers; clock arithmetic with undefined; porting Redis TTL semantics (seconds) to Deno KV (milliseconds).","solutions":["Always compute expireIn as an integer ms count: const ttlMs = Math.floor(ttlSeconds * 1000).","Validate user/config input: Number.isInteger(n) && n >= 0 before calling set.","Omit expireIn entirely (undefined is allowed) for no expiry.","Convert string config values with Number() and validate finiteness."],"exampleFix":"// before\nawait kv.set([\"session\", id], data, { expireIn: ttlSeconds }); // e.g. 3600.5 or seconds-as-ms\n\n// after\nconst expireIn = Math.floor(ttlSeconds * 1000);\nawait kv.set([\"session\", id], data, { expireIn });","handlingStrategy":"validation","validationCode":"function toExpireIn(ms: unknown): number | undefined {\n  if (ms === undefined || ms === null) return undefined;\n  const n = Math.floor(Number(ms));\n  if (!Number.isFinite(n) || n < 0) throw new Error(`invalid expireIn: ${ms}`);\n  return n;\n}\nawait kv.set(key, value, { expireIn: toExpireIn(ttlMs) });","typeGuard":"function isValidExpireIn(v: unknown): v is number { return typeof v === \"number\" && Number.isInteger(v) && v >= 0; }","tryCatchPattern":null,"preventionTips":["Convert second-based TTLs to integer ms (Math.floor(sec * 1000)).","Validate config-driven expirations with Number.isInteger at the config layer.","Omit expireIn rather than passing 0/null/strings when no expiry is wanted."],"tags":["kv","ttl","expirein","validation"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}