denoland/deno · error · Error

Limit must be a positive integer: received

Error message

Limit must be a positive integer: received ${options.limit}

What it means

Kv.list() validates its options synchronously before returning the iterator. `limit` must be a positive integer: 0, negative numbers, non-integers (1.5, NaN, Infinity) and non-number values all throw 'Limit must be a positive integer: received <value>'. Note that batchSize defaults to limit when batchSize is omitted, so an invalid limit is always reported at this check first.

Solutions

  1. Validate before calling: Number.isInteger(limit) && limit > 0.
  2. Omit the limit key entirely when you want all matching entries.
  3. Clamp computed limits: Math.max(1, Math.floor(remaining)).
  4. Parse numeric config once at startup (Number(cfg.limit)) and fail fast there with a clear message.

Example fix

// before
const it = kv.list({ prefix: ["users"] }, { limit: remaining }); // throws when remaining === 0

// after
const opts = Number.isInteger(remaining) && remaining > 0 ? { limit: remaining } : {};
const it = kv.list({ prefix: ["users"] }, opts);
Defensive patterns

Strategy: validation

Validate before calling

function listOptions(limit?: number, batchSize?: number) {
  if (limit !== undefined && (!Number.isInteger(limit) || limit <= 0)) {
    throw new Error(`invalid limit: ${limit}`);
  }
  if (batchSize !== undefined && (!Number.isInteger(batchSize) || batchSize <= 0)) {
    throw new Error(`invalid batchSize: ${batchSize}`);
  }
  return limit !== undefined ? { limit, ...(batchSize !== undefined ? { batchSize } : {}) } : {};
}

Type guard

const isPositiveInt = (v: unknown): v is number =>
  typeof v === "number" && Number.isInteger(v) && v > 0;

Prevention

When it happens

Trigger: `kv.list(selector, { limit: 0 })`, `{ limit: -1 }`, `{ limit: 2.5 }`, `{ limit: NaN }`, `{ limit: "100" }`, or a limit computed from arithmetic that can reach 0 (e.g. Math.min(pageSize, remaining) when the last page is exactly consumed). `limit: undefined` is fine — the check is skipped.

Common situations: Paginating to the final page and computing remaining=0; limit read from an env var or config file arrives as a string; user-supplied page size passed through unvalidated; spread options where limit is 0 rather than omitted.

Related errors


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

Appendix: source

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

    );
    if (!result) throw new TypeError("Failed to set value");
  }

  list(
    selector: Deno.KvListSelector,
    options: {
      limit?: number;
      batchSize?: number;
      cursor?: string;
      reverse?: boolean;
      consistency?: Deno.KvConsistencyLevel;
    } = { __proto__: null },
  ): KvListIterator {
    if (
      options.limit !== undefined &&
      (!NumberIsInteger(options.limit) || options.limit <= 0)
    ) {
      throw new Error(
        `Limit must be a positive integer: received ${options.limit}`,
      );
    }

    let batchSize = options.batchSize ?? (options.limit ?? 100);
    if (!NumberIsInteger(batchSize) || batchSize <= 0) {
      throw new Error(
        `batchSize must be a positive integer: received ${batchSize}`,
      );
    }
    if (options.batchSize === undefined && batchSize > 500) batchSize = 500;

    return new KvListIterator({
      limit: options.limit,
      selector,
      cursor: options.cursor,
      reverse: options.reverse ?? false,
      consistency: options.consistency ?? "strong",

View on GitHub (pinned to 9ad36f7a2c)