denoland/deno · error · Error

batchSize must be a positive integer: received

Error message

batchSize must be a positive integer: received ${batchSize}

What it means

Kv.list()'s batchSize (entries fetched per internal snapshot read) must be a positive integer. It defaults to `limit ?? 100` and is silently capped to 500 only when derived from limit; an explicitly provided batchSize that is 0, negative, fractional, NaN, or a non-number throws 'batchSize must be a positive integer: received <value>'. Because limit is validated first and the derived fallback comes from it, this error in practice means the batchSize you passed explicitly is invalid.

Solutions

  1. Pass a positive integer between 1 and 500 (explicit larger values are accepted but each batch snapshot reads that many entries — prefer <= 500).
  2. Validate like limit: Number.isInteger(batchSize) && batchSize > 0.
  3. Omit batchSize to inherit limit (capped at 500) or the default of 100.
  4. Coerce once at the config boundary: batchSize: Number(cfg.batchSize) with a fallback.

Example fix

// before
const it = kv.list(sel, { batchSize: cfg.batchSize }); // cfg.batchSize = "50" or 0

// after
const batchSize = Number.isInteger(Number(cfg.batchSize)) && Number(cfg.batchSize) > 0
  ? Number(cfg.batchSize)
  : undefined; // undefined -> safe default
const it = kv.list(sel, batchSize ? { batchSize } : {});
Defensive patterns

Strategy: validation

Validate before calling

const batchSize =
  cfg.batchSize === undefined || cfg.batchSize === null
    ? undefined // safe default (limit ?? 100, capped 500)
    : Number(cfg.batchSize);
if (batchSize !== undefined && (!Number.isInteger(batchSize) || batchSize <= 0)) {
  throw new Error(`bad batchSize from config: ${cfg.batchSize}`);
}
const it = kv.list(selector, batchSize ? { 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(sel, { batchSize: 0 })`, `{ batchSize: 10.5 }`, `{ batchSize: "50" }` (string), or options objects built by spreading config where batchSize is an invalid value rather than undefined (undefined falls back to limit or 100 and never throws here).

Common situations: Throughput tuning with a config-provided batchSize that arrives as a string or 0; copying limit semantics onto batchSize; schemas or defaults objects that permit 0 as a 'no preference' value.

Related errors


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

Appendix: source

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

      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",
      batchSize,
      pullBatch: this.#pullBatch(batchSize),
    });
  }

  #pullBatch(batchSize: number): (
    selector: Deno.KvListSelector,

View on GitHub (pinned to 9ad36f7a2c)