{"record":{"id":"a91330506eee59f6","repo":"tursodatabase/turso","slug":"has-record-true-but-record-returned-none","errorCode":null,"errorMessage":"has_record=true but record() returned None","messagePattern":"has_record=true but record\\(\\) returned None","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"core/storage/btree.rs","lineNumber":7629,"sourceCode":"            let page = self.stack.top_ref();\n            let contents = page.get_contents();\n            debug_assert!(\n                matches!(contents.page_type(), Ok(PageType::TableLeaf)),\n                \"save_position: table cursor with has_record=true must be on a leaf\"\n            );\n            let rowid = contents.cell_table_leaf_read_rowid(cell_idx as usize)?;\n            self.save_context(CursorContext {\n                key: CursorContextKey::TableRowId(rowid),\n                seek_op: SeekOp::GE { eq_only: true },\n            });\n            return Ok(IOResult::Done(SavePositionResult::Saved));\n        }\n        // Index btree: yield IO for overflow chains. Allocate to the actual\n        // payload size so wide-key indexes don't keep a page-sized buffer\n        // per saved cursor.\n        let cloned = {\n            let record = return_if_io!(self.record());\n            let record = record.expect(\"has_record=true but record() returned None\");\n            let payload = record.get_payload();\n            let mut owned = crate::with_btree_allocation_site!(\n                SavedCursorRecord,\n                ImmutableRecord::new(payload.len())\n            )?;\n            crate::with_btree_allocation_site!(\n                SavedCursorRecord,\n                owned.start_serialization(payload)\n            )?;\n            owned\n        };\n        self.save_context(CursorContext {\n            key: CursorContextKey::IndexKeyRowId(ImmutableRecordRef::from_owned_record(cloned)),\n            seek_op: SeekOp::GE { eq_only: true },\n        });\n        Ok(IOResult::Done(SavePositionResult::Saved))\n    }\n","sourceCodeStart":7611,"sourceCodeEnd":7647,"githubUrl":"https://github.com/tursodatabase/turso/blob/492c4a71cd7c2649e7df83da1471b74f4b1c7aa9/core/storage/btree.rs#L7611-L7647","documentation":"BTreeCursor::save_position() for index btrees (core/storage/btree.rs): after has_record() returned true, the code clones the current key with record() and expects Some. record() returning None violates the cursor contract that a valid cursor on an index leaf yields a record - e.g. the current cell cannot be parsed as an index payload.","triggerScenarios":"Saving an index cursor position (UPDATE of an indexed column, DELETE using index lookups, statement yield mid-scan) when the cursor's current cell fails to produce a record - cursor effectively parked on a non-record position (interior page or unparsable cell) while valid_state/has_record still claim a live row.","commonSituations":"Wide index keys with overflow chains, engine regressions in record() parsing for index cells, databases where corruption makes a cell unreadable.","solutions":["Report to Turso with the index definition and failing statement","Run PRAGMA integrity_check; unparsable index cells are a corruption signal","Rebuild the affected index (DROP INDEX + CREATE INDEX) if corruption of that index is confirmed","Retry on a fresh connection and updated engine build"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Before heavy UPDATE/DELETE on an indexed table, verify the index is well-formed:\nconn.execute(\"PRAGMA integrity_check\", ())?; // reports index cell corruption\n// Rebuild suspect indexes before running the workload:\nconn.execute(\"REINDEX\", ())?;","typeGuard":null,"tryCatchPattern":"let result = std::panic::catch_unwind(AssertUnwindSafe(|| {\n    conn.execute(\"UPDATE t SET indexed_col = ? WHERE ...\", [v])\n}));\nif result.is_err() { conn.close().ok(); /* reopen, REINDEX, retry */ }","preventionTips":["REINDEX after any suspected corruption or abnormal shutdown before index-heavy writes","Prefer narrow index keys; very wide keys exercise overflow chains during cursor save","Run integrity checks on databases received from external sources","Report cursor-save panics with the index DDL upstream"],"tags":["btree","index-cursor","save-position","panic"],"backgroundTag":"invalid-cursor-state","analyzedSha":"492c4a71cd7c2649e7df83da1471b74f4b1c7aa9","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}