denoland/deno · error · TypeError

Invalid backoffSchedule, max

Error message

Invalid backoffSchedule, max ${maxQueueBackoffIntervals} intervals allowed

What it means

`kv.enqueue(value, { backoffSchedule })` accepts at most 5 retry intervals (`maxQueueBackoffIntervals = 5`); the queue retries a failed message once per entry, and more than five retries have no backend support. A longer array is rejected up front with this TypeError.

Solutions

  1. Trim to the 5 most meaningful intervals (usually keep the early, fast retries and merge the tail)
  2. For more retries, re-enqueue on final failure with a fresh schedule
  3. Assert the length in a helper that builds backoff schedules

Example fix

// before
await kv.enqueue(job, { backoffSchedule: [1000, 5000, 10000, 30000, 60000, 120000] });

// after
await kv.enqueue(job, { backoffSchedule: [1000, 5000, 10000, 30000, 60000] });
Defensive patterns

Strategy: validation

Validate before calling

const MAX_INTERVALS = 5;
const backoffSchedule = rawSchedule.slice(0, MAX_INTERVALS);
await kv.enqueue(msg, { backoffSchedule });

Prevention

When it happens

Trigger: Passing six or more entries, e.g. `[1000, 5000, 10000, 30000, 60000, 120000]` — a common exponential ladder copied from another system.

Common situations: Porting retry ladders from job libraries (BullMQ, Sidekiq-style 6–8 step backoffs); schedules generated programmatically without a length bound.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/7a2accdcba9429ac. Report an issue: GitHub.

Appendix: 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 9ad36f7a2c)