{"record":{"id":"a9e7a568a2731043","repo":"tursodatabase/turso","slug":"register-holds-unexpected-value-self","errorCode":null,"errorMessage":"register holds unexpected value: {self:?}","messagePattern":"register holds unexpected value: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/vdbe/mod.rs","lineNumber":1748,"sourceCode":"    pub fn get_value(&self) -> &Value {\n        match self {\n            Register::Value(v) => v,\n            _ => self.get_value_of_other(),\n        }\n    }\n\n    /// The value of a register that holds no plain value: a record reads as\n    /// its blob, anything else is a bug. Kept out of line so that\n    /// `get_value` inlines as a tag check.\n    #[cold]\n    #[inline(never)]\n    fn get_value_of_other(&self) -> &Value {\n        match self {\n            Register::Record(r) => {\n                turso_assert!(!r.is_invalidated());\n                r.as_blob_value()\n            }\n            _ => panic!(\"register holds unexpected value: {self:?}\"),\n        }\n    }\n}\n\n#[macro_export]\nmacro_rules! must_be_btree_cursor {\n    ($cursor_id:expr, $cursor_ref:expr, $state:expr, $insn_name:expr) => {{\n        let (_, cursor_type) = $cursor_ref.get($cursor_id).unwrap();\n        if matches!(\n            cursor_type,\n            CursorType::BTreeTable(_)\n                | CursorType::BTreeIndex(_)\n                | CursorType::MaterializedView(_, _)\n        ) {\n            $crate::get_cursor!($state, $cursor_id)\n        } else {\n            panic!(\"{} on unexpected cursor\", $insn_name)\n        }","sourceCodeStart":1730,"sourceCodeEnd":1766,"githubUrl":"https://github.com/tursodatabase/turso/blob/492c4a71cd7c2649e7df83da1471b74f4b1c7aa9/core/vdbe/mod.rs#L1730-L1766","documentation":"Register::get_value() only understands the generic register representations: Register::Value, and Register::Record returned as a blob value. The Register enum also has Register::Aggregate(AggContext) — an in-flight aggregate accumulator — and reading a register that still holds one through the generic accessor panics. It means the bytecode wrote a representation the reading opcode does not support: the register's writer and reader disagree (typically a register reused for both accumulator and scalar roles).","triggerScenarios":"An opcode reads a register via get_value() while that register still holds the Register::Aggregate context from an AggStep-family instruction — e.g. aggregate accumulator registers reused across query levels (flattened subqueries, window functions), or codegen writing the wrong register index for a finalize.","commonSituations":"Aggregates and window functions in flattened or co-routine subqueries; register-allocation refactors in translate/; upgrades changing how accumulators are stored.","solutions":["Report with SQL and EXPLAIN — the register holding the AggContext was read as a plain value","Rewrite so the aggregate lives in its own query level (GROUP BY subquery) instead of being flattened","Check for column/register index mix-ups if you maintain a fork and recently touched emit_column or aggregate finalization","Upgrade"],"exampleFix":"// contributor fix: read aggregates through the aggregate path, not get_value()\nlet value = match &state.registers[target] {\n    Register::Value(v) => v.clone(),\n    Register::Record(r) => r.as_blob_value().clone(),\n    Register::Aggregate(agg) => agg.final_value().clone(), // instead of get_value() panic\n};","handlingStrategy":"type-guard","validationCode":"// before reading a register that may hold an aggregate, narrow the variant\nif matches!(state.registers[target], Register::Aggregate(_)) {\n    crate::bail_parse_error!(\"register {target} still holds an aggregate context\");\n}","typeGuard":"fn as_scalar_value(reg: &Register) -> Option<&Value> {\n    match reg {\n        Register::Value(v) => Some(v),\n        Register::Record(r) => Some(r.as_blob_value()),\n        Register::Aggregate(_) => None,\n    }\n}","tryCatchPattern":null,"preventionTips":["Match on the Register variant instead of calling get_value() on registers that can hold accumulators","Keep aggregate-in-flattened-subquery shapes in the conformance corpus","Report with EXPLAIN so register writers/readers can be traced"],"tags":["vdbe","registers","aggregate","invariant","panic","runtime"],"backgroundTag":"register-type-mismatch","analyzedSha":"492c4a71cd7c2649e7df83da1471b74f4b1c7aa9","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}