{"record":{"id":"5accd80f38700bbf","repo":"denoland/deno","slug":"delay-cannot-be-nan","errorCode":null,"errorMessage":"Delay cannot be NaN","messagePattern":"Delay cannot be NaN","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/kv/01_db.ts","lineNumber":76,"sourceCode":"\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  }\n}\n\nconst maxQueueBackoffIntervals = 5;\nconst maxQueueBackoffInterval = 60 * 60 * 1000;\n","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/kv/01_db.ts#L58-L94","documentation":"The final check in validateQueueDelay: delay must not be NaN. NaN arises from parsing garbage ('parseInt(\"soon\")'), arithmetic on undefined (undefined - Date.now()), or JSON payloads with null coerced oddly. Note the checks are ordered: NaN passes the < 0 and > max comparisons as false and is only caught by the explicit NumberIsNaN test.","triggerScenarios":"kv.enqueue(msg, { delay: undefined - Date.now() }); delay: Number.parseInt(process.env.DELAY_MS, 10) with a non-numeric env var; delay: NaN from JSON.parse of a missing field defaulted incorrectly; delay: +\"1e3 ms\" parse failures.","commonSituations":"Optional config omitted then used in arithmetic; environment variables with units ('500ms') fed to parseInt; schema-less input payloads; defaulting with || where 0 was intended.","solutions":["Validate/coerce before calling: const d = Number(delay); if (!Number.isFinite(d)) throw new Error('bad delay').","Default missing values explicitly: const delay = opts.delay ?? 0.","Strip units from config values or parse strictly (Number('500') not parseInt('500ms'))."],"exampleFix":"// before\nconst delay = config.waitMs - Date.now(); // config.waitMs undefined -> NaN\nawait kv.enqueue(job, { delay });\n\n// after\nconst delay = Math.max(0, (config.waitMs ?? Date.now()) - Date.now());\nawait kv.enqueue(job, { delay });","handlingStrategy":"validation","validationCode":"const delay = Number(opts.delay ?? 0);\nif (!Number.isFinite(delay)) throw new Error(`invalid delay: ${opts.delay}`);\nawait kv.enqueue(job, { delay });","typeGuard":"function isFiniteNonNegative(n: unknown): n is number { return typeof n === \"number\" && Number.isFinite(n) && n >= 0; }","tryCatchPattern":null,"preventionTips":["Default optional numeric options explicitly (?? 0), don't do arithmetic on undefined.","Parse env/config numbers with Number() and reject NaN at the boundary.","Validate untrusted JSON numerics before passing them to KV."],"tags":["kv","queue","nan","validation"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}