{"record":{"id":"95f7a2fc2b9d5fe6","repo":"denoland/deno","slug":"cannot-get-cursor-before-first-iteration","errorCode":null,"errorMessage":"Cannot get cursor before first iteration","messagePattern":"Cannot get cursor before first iteration","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ext/kv/01_db.ts","lineNumber":904,"sourceCode":"        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\n    return this.#cursorGen();\n  }\n\n  async next(): Promise<IteratorResult<Deno.KvEntry<unknown>>> {\n    // Fused or limit exceeded\n    if (\n      this.#done ||\n      (this.#limit !== undefined && this.#count >= this.#limit)\n    ) {\n      return { done: true, value: undefined };\n    }\n\n    // Attempt to fill the buffer\n    if (!this.#entries?.length && !this.#lastBatch) {\n      const batch = await this.#pullBatch(\n        this.#selector,","sourceCodeStart":886,"sourceCodeEnd":922,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/kv/01_db.ts#L886-L922","documentation":"KvListIterator.cursor exposes the opaque resumption cursor only after the first batch has been pulled — the iterator installs its internal cursor generator when the first next() resolves. Reading .cursor on a fresh, un-advanced iterator throws Error('Cannot get cursor before first iteration'). After iteration starts, the cursor encodes the last-delivered key so a later list() call can resume.","triggerScenarios":"`const it = kv.list(sel); it.cursor;` — inspecting, logging, or persisting the cursor before any `await it.next()` or for-await loop has advanced the iterator.","commonSituations":"Pagination scaffolds that store the cursor immediately after creating the iterator; refactors that move the cursor read above the iteration loop; debugging with console.log(it.cursor).","solutions":["Advance once before reading: `await it.next()` — the cursor is valid afterwards, even if that call returned done.","In for-await loops, read the cursor after the loop finishes (it is set by then).","Model pagination as: iterate a batch to completion, then persist the cursor for the next invocation."],"exampleFix":"// before\nconst it = kv.list({ prefix: [\"users\"] });\nconst cursor = it.cursor; // Error: Cannot get cursor before first iteration\n\n// after\nconst it = kv.list({ prefix: [\"users\"] });\nfor await (const entry of it) { /* ... */ }\nconst cursor = it.cursor; // resumption point after the last yielded key","handlingStrategy":"validation","validationCode":"async function listPage(kv: Deno.Kv, selector: Deno.KvListSelector, cursor?: string) {\n  const it = kv.list(selector, cursor ? { cursor } : {});\n  const entries: Deno.KvEntry<unknown>[] = [];\n  for await (const e of it) entries.push(e); // advancing sets the cursor\n  return { entries, nextCursor: it.cursor }; // safe: iteration happened\n}","typeGuard":"const hasStarted = (it: Deno.KvListIterator<unknown>): boolean => {\n  try {\n    it.cursor;\n    return true;\n  } catch {\n    return false;\n  }\n};","tryCatchPattern":"let cursor: string;\ntry {\n  cursor = it.cursor;\n} catch (e) {\n  if (e instanceof Error && e.message === \"Cannot get cursor before first iteration\") {\n    await it.next(); // advance once, then read\n    cursor = it.cursor;\n  } else throw e;\n}","preventionTips":["Never read .cursor before the first next() or for-await iteration","Persist cursors only after finishing a batch, from a single pagination helper","Read the cursor after the for-await loop completes — it is set by then"],"tags":["deno-kv","cursor","pagination","iterator","kv-list"],"backgroundTag":"pagination-cursor-unavailable","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"}