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
- Pass a positive integer between 1 and 500 (explicit larger values are accepted but each batch snapshot reads that many entries — prefer <= 500).
- Validate like limit: Number.isInteger(batchSize) && batchSize > 0.
- Omit batchSize to inherit limit (capped at 500) or the default of 100.
- 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
- Parse batchSize config once with Number() and a sane default
- Keep explicit batchSize in 1..500
- Treat 0 in config as 'use default' (map to undefined), never pass it through
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
- Limit must be a positive integer: received
- Cannot get cursor before first iteration
- Selector can not specify both 'start' and 'end' key when…
- Selector must specify either 'prefix' or both 'start' and…
- Delay cannot be greater than 30 days: received
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)