denoland/deno · error · TypeError

Invalid backoffSchedule, interval at index ${i} is invalid

Error message

Invalid backoffSchedule, interval at index ${i} is invalid

What it means

Each element of kv.enqueue's backoffSchedule must satisfy: interval >= 0, interval <= 60 * 60 * 1000 (1 hour, maxQueueBackoffInterval), and not NaN. The per-element check reports the first offending index so you can locate the bad value.

Source

Thrown at ext/kv/01_db.ts:107

  }
}

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;
  value: RawValue;
  versionstamp: string;
}

type RawValue = {
  kind: "v8";
  value: Uint8Array;
} | {
  kind: "bytes";
  value: Uint8Array;

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Clamp each interval: backoffSchedule.map((i) => Math.min(Math.max(0, i), 3600000)) after validating they are numbers.
  2. Cap exponential generation so the last step stays <= 1h.
  3. Validate the array before enqueue: every element Number.isInteger-ish, finite, in [0, 3600000].

Example fix

// before
const backoffSchedule = [100, 1000, 10000, 100000, 1000000, 7200000]; // 6th > 1h
await kv.enqueue(job, { backoffSchedule });

// after
const backoffSchedule = [100, 1000, 10000, 100000, 3600000].map((i) =>
  Math.min(Math.max(0, i), 60 * 60 * 1000)
);
await kv.enqueue(job, { backoffSchedule });
Defensive patterns

Strategy: validation

Validate before calling

const MAX_MS = 60 * 60 * 1000;
const backoffSchedule = rawBackoff
  .map((i) => Math.floor(Number(i)))
  .filter((i) => Number.isFinite(i) && i >= 0)
  .map((i) => Math.min(i, MAX_MS))
  .slice(0, 5);
await kv.enqueue(job, { backoffSchedule });

Type guard

function isCleanBackoffSchedule(arr: unknown): arr is number[] { const MAX = 60 * 60 * 1000; return Array.isArray(arr) && arr.every((i) => typeof i === "number" && !Number.isNaN(i) && i >= 0 && i <= MAX); }

Prevention

When it happens

Trigger: backoffSchedule: [100, -500, 1000] with a negative entry; [1000, 7200000] exceeding one hour; [Number('soon')] or [undefined] coercing to NaN; schedules expressed in seconds (e.g. 86400) being interpreted as ms is fine but the reverse - ms values like 3600001 - trip the cap; entries from JSON strings.

Common situations: Exponential backoff loops exceeding 1h at later steps (2^22+ ms); retry policies copied from cloud SDKs allowing day-long waits; sign errors in computed intervals; unvalidated user-configured retry arrays.

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/481f2a29c698a72b. Report an issue: GitHub.