{"record":{"id":"1fd9c2dca513aac6","repo":"tursodatabase/turso","slug":"invalid-position-to-read-current-mvcc-row","errorCode":null,"errorMessage":"invalid position to read current mvcc row","messagePattern":"invalid position to read current mvcc row","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/mvcc/cursor.rs","lineNumber":726,"sourceCode":"                })?;\n                Ok(IOResult::Done(Some(record_ref)))\n            }\n            CursorPosition::BeforeFirst => {\n                // Before first is not a valid position, so we return none.\n                Ok(IOResult::Done(None))\n            }\n            CursorPosition::End => Ok(IOResult::Done(None)),\n        }\n    }\n\n    pub fn read_mvcc_current_row(&self) -> Result<Option<Row>> {\n        let (row_id, versions) = match &self.current_pos {\n            CursorPosition::Loaded {\n                row_id,\n                in_btree,\n                versions,\n            } if !in_btree => (row_id, versions),\n            _ => panic!(\"invalid position to read current mvcc row\"),\n        };\n        // Scan path: the range iterator already resolved this row's version\n        // chain, so read it directly instead of a second skiplist lookup.\n        if let Some(versions) = versions {\n            return self.db.read_visible_from_versions(self.tx_id, versions);\n        }\n        let maybe_index_id = match &self.mv_cursor_type {\n            MvccCursorType::Index(_) => Some(self.table_id),\n            MvccCursorType::Table => None,\n        };\n        self.db\n            .read_from_table_or_index(self.tx_id, row_id, maybe_index_id)\n    }\n\n    pub fn close(self) -> Result<()> {\n        Ok(())\n    }\n","sourceCodeStart":708,"sourceCodeEnd":744,"githubUrl":"https://github.com/tursodatabase/turso/blob/492c4a71cd7c2649e7df83da1471b74f4b1c7aa9/core/mvcc/cursor.rs#L708-L744","documentation":"MvccCursor::read_mvcc_current_row is only valid while the cursor sits at CursorPosition::Loaded with in_btree == false - positioned by the MVCC scan path where the row's version chain is already resolved. Calling it at Start/End, at an in-btree position, or before advancing panics; it is an internal API-contract check in core/mvcc.","triggerScenarios":"Executor code calling read_mvcc_current_row before the first next() step, after exhaustion, or while the cursor is dual-positioned in the btree half of the MVCC cursor.","commonSituations":"Contributor changes to MVCC cursor stepping; new scan operators reusing the cursor API incorrectly.","solutions":["Advance the cursor (next) until it reports a row before reading","Branch on the current position and only read when Loaded with in_btree == false","If reached without touching core code, report with the query and MVCC setup"],"exampleFix":"// before\nlet row = cursor.read_mvcc_current_row()?; // called before any next()\n\n// after\nwhile let IOResult::Done(Some(_)) = return_if_io!(cursor.next()) {\n    if let Some(row) = cursor.read_mvcc_current_row()? { /* consume row */ }\n}","handlingStrategy":"validation","validationCode":"// only read the current row while positioned by the scan path\nmatch cursor.get_current_pos() {\n    CursorPosition::Loaded { in_btree: false, .. } => cursor.read_mvcc_current_row(),\n    _ => Ok(None),\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["read_mvcc_current_row is a post-advance API: always step the cursor first","In review, keep position-shape checks adjacent to MVCC cursor call sites"],"tags":["mvcc","cursor","state-machine","internal","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"}