tursodatabase/turso · error
register holds unexpected value: {self:?}
Error message
register holds unexpected value: {self:?} What it means
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).
Source
Thrown at core/vdbe/mod.rs:1748
pub fn get_value(&self) -> &Value {
match self {
Register::Value(v) => v,
_ => self.get_value_of_other(),
}
}
/// The value of a register that holds no plain value: a record reads as
/// its blob, anything else is a bug. Kept out of line so that
/// `get_value` inlines as a tag check.
#[cold]
#[inline(never)]
fn get_value_of_other(&self) -> &Value {
match self {
Register::Record(r) => {
turso_assert!(!r.is_invalidated());
r.as_blob_value()
}
_ => panic!("register holds unexpected value: {self:?}"),
}
}
}
#[macro_export]
macro_rules! must_be_btree_cursor {
($cursor_id:expr, $cursor_ref:expr, $state:expr, $insn_name:expr) => {{
let (_, cursor_type) = $cursor_ref.get($cursor_id).unwrap();
if matches!(
cursor_type,
CursorType::BTreeTable(_)
| CursorType::BTreeIndex(_)
| CursorType::MaterializedView(_, _)
) {
$crate::get_cursor!($state, $cursor_id)
} else {
panic!("{} on unexpected cursor", $insn_name)
}View on GitHub (pinned to 492c4a71cd)
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
Example fix
// contributor fix: read aggregates through the aggregate path, not get_value()
let value = match &state.registers[target] {
Register::Value(v) => v.clone(),
Register::Record(r) => r.as_blob_value().clone(),
Register::Aggregate(agg) => agg.final_value().clone(), // instead of get_value() panic
}; Defensive patterns
Strategy: type-guard
Validate before calling
// before reading a register that may hold an aggregate, narrow the variant
if matches!(state.registers[target], Register::Aggregate(_)) {
crate::bail_parse_error!("register {target} still holds an aggregate context");
} Type guard
fn as_scalar_value(reg: &Register) -> Option<&Value> {
match reg {
Register::Value(v) => Some(v),
Register::Record(r) => Some(r.as_blob_value()),
Register::Aggregate(_) => None,
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Aggregates and window functions in flattened or co-routine subqueries; register-allocation refactors in translate/; upgrades changing how accumulators are stored.
Related errors
- group_concat accumulator must be a Text value
- OpenRead on pseudo cursor
- Rewind on non-btree/materialized-view cursor
- unexpected cursor type
- Next on non-btree/materialized-view cursor
AI-assisted analysis of tursodatabase/turso@492c4a71cd (2026-08-20).
Data as JSON: /api/errors/a9e7a568a2731043.
Report an issue: GitHub.