tursodatabase/turso · error

Insn:Column on virtual table cursor, use Insn:VColumn instea

Error message

Insn:Column on virtual table cursor, use Insn:VColumn instead

What it means

Insn::Column executed on a CursorType::VirtualTable cursor. Virtual table columns must be read with Insn::VColumn, which invokes the module's column callback; generic Column decodes a B-tree record and cannot work on a module cursor. The translator emitted the generic opcode against a virtual table — a codegen bug for that statement shape.

Source

Thrown at core/vdbe/execute.rs:2314

                state.registers[dest].set_null();
            }
        }
        CursorType::Pseudo(_) => {
            let content_reg = crate::get_cursor!(state, cursor_id)
                .as_pseudo_mut()
                .content_reg();
            return op_column_fetch_pseudo(state, content_reg, column, dest);
        }
        CursorType::IndexMethod(..) => {
            let cursor = state.cursors[cursor_id]
                .as_mut()
                .expect("cursor should exist");
            let cursor = cursor.as_index_method_mut();
            let value = return_if_io!(state, cursor.query_column(column));
            state.registers[dest].set_value(value);
        }
        CursorType::VirtualTable(_) => {
            panic!("Insn:Column on virtual table cursor, use Insn:VColumn instead");
        }
    }
    Ok(InsnFunctionStepResult::Step)
}

fn apply_column_default(default: &Option<Value>, reg: &mut Register) -> Result<()> {
    let Some(default) = default else {
        reg.set_null();
        return Ok(());
    };
    match (default, reg) {
        (Value::Text(new_text), Register::Value(Value::Text(existing_text))) => {
            existing_text.do_extend(new_text)?;
        }
        (Value::Blob(new_blob), Register::Value(Value::Blob(existing_blob))) => {
            existing_blob.do_extend(new_blob)?;
        }
        (default, reg) => {

View on GitHub (pinned to 492c4a71cd)

Solutions

  1. Report with the module and SQL — translation must emit VColumn for vtab cursors
  2. Remove RETURNING or wrap access so the vtab is read through a plain SELECT list
  3. Query the vtab directly instead of through the layered view/trigger
  4. Upgrade

Example fix

// contributor fix in translate/ column emission: branch on the cursor kind
if matches!(cursor_type, CursorType::VirtualTable(_)) {
    program.emit_insn(Insn::VColumn { cursor_id, column, dest_reg });
} else {
    program.emit_insn(Insn::Column { cursor_id, column, dest_reg });
}
Defensive patterns

Strategy: validation

Validate before calling

// generic Column must not read virtual table cursors
for insn in &program.insns {
    if let Insn::Column { cursor_id, .. } = insn {
        if matches!(program.cursor_ref.get(*cursor_id).map(|(_, t)| t), Some(CursorType::VirtualTable(_))) {
            crate::bail_parse_error!("Column on virtual table cursor; VColumn required");
        }
    }
}

Prevention

When it happens

Trigger: SELECT lists, RETURNING clauses, or trigger/view bodies over virtual tables in code paths that build column reads generically — e.g. RETURNING on vtab DML, or views/triggers layered over vtab modules like csv, fts, or pragma-backed tables.

Common situations: Extensions and catalog-style vtabs combined with RETURNING, views, or triggers; engine versions where column emission was unified.

Related errors


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