{"record":{"id":"e94de3ac58d94f3a","repo":"denoland/deno","slug":"selector-must-specify-either-prefix-or-both-sta","errorCode":null,"errorMessage":"Selector must specify either 'prefix' or both 'start' and 'end' key","messagePattern":"Selector must specify either 'prefix' or both 'start' and 'end' key","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/kv/01_db.ts","lineNumber":888,"sourceCode":"    }\n    if (prefix) {\n      if (start && end) {\n        throw new TypeError(\n          \"Selector can not specify both 'start' and 'end' key when specifying 'prefix'\",\n        );\n      }\n      if (start) {\n        this.#selector = { prefix, start };\n      } else if (end) {\n        this.#selector = { prefix, end };\n      } else {\n        this.#selector = { prefix };\n      }\n    } else {\n      if (start && end) {\n        this.#selector = { start, end };\n      } else {\n        throw new TypeError(\n          \"Selector must specify either 'prefix' or both 'start' and 'end' key\",\n        );\n      }\n    }\n    ObjectFreeze(this.#selector);\n    this.#pullBatch = pullBatch;\n    this.#limit = limit;\n    this.#reverse = reverse;\n    this.#consistency = consistency;\n    this.#batchSize = batchSize;\n    this.#cursorGen = cursor ? () => cursor : null;\n  }\n\n  get cursor(): string {\n    if (this.#cursorGen === null) {\n      throw new Error(\"Cannot get cursor before first iteration\");\n    }\n","sourceCodeStart":870,"sourceCodeEnd":906,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/kv/01_db.ts#L870-L906","documentation":"Without a prefix, a selector must define a complete range: both start and end keys. Anything else — {start} alone, {end} alone, or an empty selector {} — throws TypeError(\"Selector must specify either 'prefix' or both 'start' and 'end' key\"). The rationale: prefix bounds the range by itself, while a half-open pair would request an unbounded scan of the keyspace.","triggerScenarios":"`kv.list({ start: [\"a\"] })`; `kv.list({ end: [\"z\"] })`; `kv.list({})`; selectors built from optional fields where all of prefix/start/end ended up absent (undefined values are ignored by the hasOwn checks).","commonSituations":"Filter UIs where both bounds are optional and the user supplies only one; spreading a config that may omit keys; porting from cursor-only iteration models that have no range concept.","solutions":["Validate the selector shape before calling list(): require prefix or both bounds.","When a half-open range is wanted, synthesize the missing bound: start defaults to [] (very first key) or end to a sentinel key above your last real key.","Type selectors as a discriminated union (prefix form vs range form) so TypeScript enforces the shape."],"exampleFix":"// before\nkv.list({ start: [\"2026\"] }); // throws\n\n// after\nkv.list({ start: [\"2026\"], end: [\"2027\"] });\n// or scope with a prefix instead:\nkv.list({ prefix: [\"2026\"] });","handlingStrategy":"type-guard","validationCode":"function normalizeSelector(s: Record<string, undefined | Deno.KvKey>) {\n  const has = (k: string) => s[k] !== undefined;\n  if (has(\"prefix\")) return { prefix: s.prefix! };\n  if (has(\"start\") && has(\"end\")) return { start: s.start!, end: s.end! };\n  // synthesize missing bounds for half-open ranges\n  if (has(\"start\")) return { start: s.start!, end: [\"\\uffff\"] };\n  if (has(\"end\")) return { start: [], end: s.end! };\n  return { prefix: [] }; // full scan, explicitly\n}","typeGuard":"type ListSelector =\n  | { prefix: Deno.KvKey; start?: Deno.KvKey; end?: Deno.KvKey }\n  | { start: Deno.KvKey; end: Deno.KvKey };\n\nconst isCompleteSelector = (s: ListSelector): boolean =>\n  \"prefix\" in s || (\"start\" in s && \"end\" in s);","tryCatchPattern":null,"preventionTips":["Require prefix or both bounds before calling kv.list()","Validate user-supplied filter shapes and synthesize missing bounds deliberately","Use a discriminated-union type for selectors so TS rejects half-open forms"],"tags":["deno-kv","kv-list","selector","range","validation"],"backgroundTag":"invalid-range-selector","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}