tursodatabase/turso · error

unexpected cursor type in op_column_range_fetch body ({other

Error message

unexpected cursor type in op_column_range_fetch body ({other:?})

What it means

op_column_range_fetch (bulk extraction of a column range into registers) matched a cursor kind it cannot handle — the catch-all arm after the B-tree record and pseudo-record cases. The opcode was emitted for a cursor that has no record payload to decode (sorter, virtual table, index-method, etc.), so the program is inconsistent with the cursor's kind.

Source

Thrown at core/vdbe/execute.rs:2504

                let (left, right) = state.registers.split_at_mut(dest + count);
                (&mut right[content_reg - (dest + count)], &mut left[dest..])
            };
            match content {
                Register::Record(record) => {
                    let filled = record
                        .iter()?
                        .decode_into_registers_after(start_column, dest_regs)?;
                    if filled < count {
                        crate::bail_parse_error!("pseudo-cursor column out of range for record");
                    }
                }
                other => {
                    crate::bail_parse_error!("non-register register in pseudo-record ({other:?})")
                }
            }
        }
        other => {
            panic!("unexpected cursor type in op_column_range_fetch body ({other:?})")
        }
    }
    Ok(InsnFunctionStepResult::Step)
}

pub fn op_column_has_field(
    program: &Program,
    state: &mut ProgramState,
    insn: &Insn,
    _pager: &Arc<Pager>,
) -> InsnResult {
    load_insn!(
        ColumnHasField {
            cursor_id,
            column,
            target_pc,
        },
        insn

View on GitHub (pinned to 492c4a71cd)

Solutions

  1. Report with SQL — the range-fetch opcode must only target record-backed cursors
  2. Narrow the selected column list to see whether the plan falls back to per-column Column opcodes
  3. Move the wide projection into its own query level
  4. Upgrade
Defensive patterns

Strategy: type-guard

Validate before calling

// range fetch requires a record-backed cursor
if let Insn::ColumnRangeFetch { cursor_id, .. } = insn {
    match program.cursor_ref.get(*cursor_id).map(|(_, t)| t) {
        Some(CursorType::BTreeTable(_)) | Some(CursorType::BTreeIndex(_)) | Some(CursorType::Pseudo(_)) => {}
        other => crate::bail_parse_error!("ColumnRangeFetch on non-record cursor ({other:?})"),
    }
}

Type guard

fn record_backed(t: &CursorType) -> bool {
    matches!(t, CursorType::BTreeTable(_) | CursorType::BTreeIndex(_) | CursorType::Pseudo(_))
}

Prevention

When it happens

Trigger: Plans using the bulk column-range fetch (wide column lists) where the target cursor was allocated as a sorter or vtab cursor — commonly after subquery flattening, or when column-range emission was extended to new statement shapes.

Common situations: Wide SELECT lists over many columns; queries over vtabs or sorted subqueries after upgrades enabling the column-range opcode.

Related errors


AI-assisted analysis of tursodatabase/turso@492c4a71cd (2026-08-20). Data as JSON: /api/errors/28779a6710c49bbf. Report an issue: GitHub.