denoland/deno · error · TypeError
Selector can not specify both 'start' and 'end' key when…
Error message
Selector can not specify both 'start' and 'end' key when specifying 'prefix'
What it means
A Kv list selector may combine prefix with at most one range bound: {prefix, start?} or {prefix, end?}. Specifying prefix together with both start and end throws TypeError("Selector can not specify both 'start' and 'end' key when specifying 'prefix'") — the prefix already fixes the bounds of the range, so a third constraint is contradictory.
Solutions
- Drop the prefix and keep { start, end } when you need an explicit range.
- Keep the prefix with exactly one bound: { prefix, end } pages up to a boundary; { prefix, start } pages from one.
- Build selectors through a helper that enforces at most one bound alongside prefix.
Example fix
// before
kv.list({ prefix: ["users"], start: ["users", "a"], end: ["users", "z"] });
// after
kv.list({ start: ["users", "a"], end: ["users", "z"] });
// or, if scoping to the prefix is what you want:
kv.list({ prefix: ["users"], end: ["users", "z"] }); Defensive patterns
Strategy: validation
Validate before calling
function makeSelector(s: Record<string, unknown>) {
const has = (k: string) => Object.hasOwn(s, k) && s[k] !== undefined;
if (has("prefix") && has("start") && has("end")) {
const { start, end, ...rest } = s; // drop prefix instead: full range wins
return { start, end };
}
return s;
}
kv.list(makeSelector(rawSelector)); Type guard
type SafeSelector =
| { prefix: Deno.KvKey; start?: Deno.KvKey }
| { prefix: Deno.KvKey; end?: Deno.KvKey }
| { start: Deno.KvKey; end: Deno.KvKey };
const isSafeSelector = (s: SafeSelector): boolean =>
!("prefix" in s && "start" in s && "end" in s); Prevention
- Never attach both start and end alongside prefix
- Scope tenant/datacenter subsets with prefix + at most one bound
- Centralize selector construction in one validated helper
When it happens
Trigger: `kv.list({ prefix: ["users"], start: ["users", "a"], end: ["users", "z"] })` — selectors assembled generically from whatever fields are present, or migrated from a {start, end} selector to prefix without removing the old bounds.
Common situations: Generic query builders attaching every provided option; config objects reused across queries; refactors that add a prefix for scoping but leave legacy bounds in place.
Related errors
- Selector must specify either 'prefix' or both 'start' and…
- batchSize must be a positive integer: received
- Cannot get cursor before first iteration
- Limit must be a positive integer: received
- Delay cannot be greater than 30 days: received
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/72d5e0982848458a.
Report an issue: GitHub.
Appendix: source
Thrown at ext/kv/01_db.ts:873
) => Promise<Deno.KvEntry<unknown>[]>;
},
) {
super();
let prefix: Deno.KvKey | undefined;
let start: Deno.KvKey | undefined;
let end: Deno.KvKey | undefined;
if (ObjectHasOwn(selector, "prefix") && selector.prefix !== undefined) {
prefix = ObjectFreeze(ArrayPrototypeSlice(selector.prefix));
}
if (ObjectHasOwn(selector, "start") && selector.start !== undefined) {
start = ObjectFreeze(ArrayPrototypeSlice(selector.start));
}
if (ObjectHasOwn(selector, "end") && selector.end !== undefined) {
end = ObjectFreeze(ArrayPrototypeSlice(selector.end));
}
if (prefix) {
if (start && end) {
throw new TypeError(
"Selector can not specify both 'start' and 'end' key when specifying 'prefix'",
);
}
if (start) {
this.#selector = { prefix, start };
} else if (end) {
this.#selector = { prefix, end };
} else {
this.#selector = { prefix };
}
} else {
if (start && end) {
this.#selector = { start, end };
} else {
throw new TypeError(
"Selector must specify either 'prefix' or both 'start' and 'end' key",
);
}View on GitHub (pinned to 9ad36f7a2c)