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
- Validate before calling: Number.isInteger(limit) && limit > 0.
- Omit the limit key entirely when you want all matching entries.
- Clamp computed limits: Math.max(1, Math.floor(remaining)).
- 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
- Validate limits at the config boundary with Number.isInteger and > 0
- Omit the limit key (undefined) rather than passing 0 for 'no limit'
- Clamp computed limits with Math.max(1, Math.floor(x))
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
- batchSize 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/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)