denoland/deno · error · TypeError
expireIn must be a non-negative integer: received
Error message
expireIn must be a non-negative integer: received ${expireIn} What it means
`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).
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)
Example fix
// before
await kv.set("session", data, { expireIn: ttlSeconds * 1.5 }); // fractional -> throws
// after
await kv.set("session", data, { expireIn: Math.max(0, Math.round(ttlSeconds * 1000)) }); Defensive patterns
Strategy: validation
Validate before calling
if (expireIn !== undefined && (!Number.isInteger(expireIn) || expireIn < 0)) {
throw new Error(`invalid expireIn: ${expireIn}`);
}
await kv.set(key, value, { expireIn }); Type guard
const isValidExpireIn = (v: unknown): v is number | undefined => v === undefined || (typeof v === "number" && Number.isInteger(v) && v >= 0);
Prevention
- 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
When it happens
Trigger: `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).
Common situations: TTLs derived from user input or config parsing; mixing seconds and milliseconds; math on undefined values passed straight into `kv.set`.
Related errors
- Delay cannot be greater than 30 days: received
- Delay cannot be NaN
- Delay must be >= 0: received
- Invalid backoffSchedule, interval at index
- Invalid backoffSchedule, max
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/e9123eb251c2b6d4.
Report an issue: GitHub.
Appendix: source
Thrown at ext/kv/01_db.ts:86
throw new TypeError(`Delay must be >= 0: received ${delay}`);
}
if (delay > maxQueueDelay) {
throw new TypeError(
`Delay cannot be greater than 30 days: received ${delay}`,
);
}
if (NumberIsNaN(delay)) {
throw new TypeError("Delay cannot be NaN");
}
}
function validateExpireIn(expireIn: number | undefined) {
if (expireIn === undefined) return;
// Reject NaN, Infinity, fractional and negative values. A non-finite
// expireIn otherwise reaches the native layer and overflows when computing
// the absolute expiry, panicking the process.
if (!NumberIsInteger(expireIn) || expireIn < 0) {
throw new TypeError(
`expireIn must be a non-negative integer: received ${expireIn}`,
);
}
}
const maxQueueBackoffIntervals = 5;
const maxQueueBackoffInterval = 60 * 60 * 1000;
function validateBackoffSchedule(backoffSchedule: number[]) {
if (backoffSchedule.length > maxQueueBackoffIntervals) {
throw new TypeError(
`Invalid backoffSchedule, max ${maxQueueBackoffIntervals} intervals allowed`,
);
}
for (let i = 0; i < backoffSchedule.length; ++i) {
const interval = backoffSchedule[i];
if (
interval < 0 || interval > maxQueueBackoffInterval ||View on GitHub (pinned to 9ad36f7a2c)