denoland/deno · error · TypeError
Invalid backoffSchedule, max ${maxQueueBackoffIntervals} int
Error message
Invalid backoffSchedule, max ${maxQueueBackoffIntervals} intervals allowed What it means
kv.enqueue accepts options.backoffSchedule, an array of retry intervals (ms) applied when a message handler fails. Deno limits it to at most 5 intervals (maxQueueBackoffIntervals); a longer array is rejected before the enqueue op runs.
Source
Thrown at ext/kv/01_db.ts:97
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 ||
NumberIsNaN(interval)
) {
throw new TypeError(
`Invalid backoffSchedule, interval at index ${i} is invalid`,
);
}
}
}
interface RawKvEntry {
key: Deno.KvKey;View on GitHub (pinned to 89f33cbef2)
Solutions
- Trim to at most 5 intervals: backoffSchedule.slice(0, 5).
- After the last interval, use the message's dead-letter behavior (a non-retryable failure path) instead of more stages.
- When generating, cap the loop at 5 iterations.
Example fix
// before
const backoffSchedule = Array.from({ length: 8 }, (_, i) => 2 ** i * 100);
await kv.enqueue(job, { backoffSchedule });
// after
const backoffSchedule = Array.from({ length: 5 }, (_, i) => 2 ** i * 100);
await kv.enqueue(job, { backoffSchedule }); Defensive patterns
Strategy: validation
Validate before calling
const MAX_INTERVALS = 5;
const backoffSchedule = rawSchedule
.filter((i) => Number.isFinite(i) && i >= 0)
.slice(0, MAX_INTERVALS);
await kv.enqueue(job, { backoffSchedule }); Type guard
function isValidBackoffSchedule(arr: unknown): arr is number[] { return Array.isArray(arr) && arr.length <= 5 && arr.every((i) => typeof i === "number" && Number.isFinite(i) && i >= 0 && i <= 60 * 60 * 1000); } Prevention
- Cap generated exponential schedules at 5 entries.
- Design retry ladders around the 5-interval limit plus a dead-letter path.
- Validate imported retry policies before use.
When it happens
Trigger: kv.enqueue(msg, { backoffSchedule: [100, 1000, 10000, 60000, 300000, 1800000] }) with six entries; generating the schedule programmatically (e.g. exponential backoff loop of 7 steps); merging retry policies from another system with more stages.
Common situations: Porting RabbitMQ/SQS retry ladders with many stages; auto-generated exponential schedules (2^n for n up to 10); combining default plus custom schedules via array spread/concat.
Related errors
- Invalid backoffSchedule, interval at index ${i} is invalid
- Delay cannot be greater than 30 days: received ${delay}
- Delay must be >= 0: received ${delay}
- Delay cannot be NaN
- Multipart part has too many headers
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/7a2accdcba9429ac.
Report an issue: GitHub.