risingwavelabs/risingwave · error

no value found at column: {}, index: {}

Error message

no value found at column: {}, index: {}

What it means

Inside the `handle_data_type!` macro (first arm, direct Rust-type conversion), `row.take_opt` returned `None`, meaning the MySQL row has no value slot at the requested column index. The macro converts this absence into an anyhow error identifying the column name and index. Note: the DECLARED/USED metadata pointing at ci/scripts Python files is unrelated noise; the actual throw site is this Rust macro in src/connector/src/parser/mysql.rs.

Source

Thrown at src/connector/src/parser/mysql.rs:38

use risingwave_common::log::LogSuppressor;
use risingwave_common::row::OwnedRow;
use thiserror_ext::AsReport;

use crate::parser::utils::log_error;

static LOG_SUPPRESSOR: LazyLock<LogSuppressor> = LazyLock::new(LogSuppressor::default);
use anyhow::anyhow;
use chrono::NaiveDate;
use risingwave_common::bail;
use risingwave_common::types::{
    DataType, Date, Datum, Decimal, JsonbVal, ScalarImpl, Time, Timestamp, Timestamptz,
};
use rust_decimal::Decimal as RustDecimal;

macro_rules! handle_data_type {
    ($row:expr, $i:expr, $name:expr, $typ:ty) => {{
        match $row.take_opt::<Option<$typ>, _>($i) {
            None => bail!("no value found at column: {}, index: {}", $name, $i),
            Some(Ok(val)) => Ok(val.map(|v| ScalarImpl::from(v))),
            Some(Err(e)) => Err(anyhow::Error::new(e.clone())
                .context("failed to deserialize MySQL value into rust value")
                .context(format!(
                    "column: {}, index: {}, rust_type: {}",
                    $name,
                    $i,
                    stringify!($typ),
                ))),
        }
    }};
    ($row:expr, $i:expr, $name:expr, $typ:ty, $rw_type:ty) => {{
        match $row.take_opt::<Option<$typ>, _>($i) {
            None => bail!("no value found at column: {}, index: {}", $name, $i),
            Some(Ok(val)) => Ok(val.map(|v| ScalarImpl::from(<$rw_type>::from(v)))),
            Some(Err(e)) => Err(anyhow::Error::new(e.clone())
                .context("failed to deserialize MySQL value into rw value")
                .context(format!(

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Re-fetch the table schema so column metadata matches the current MySQL table, then retry the snapshot.
  2. Verify the index passed to the macro comes from iterating the same column list used to read the row.
  3. Check for concurrent ALTER TABLE on the source during snapshot; pause DDL or re-create the source.
Defensive patterns

Strategy: validation

Validate before calling

assert!(idx < row.as_ref().columns().len(), "column index {} out of range for MySQL row ({} cols)", idx, row.as_ref().columns().len());

Try / catch

match decode_col(row, idx, name) {
    Err(e) if e.to_string().contains("no value found at column") => handle_schema_drift(),
    other => other,
}

Prevention

When it happens

Trigger: The macro is invoked with a column index `$i` that exceeds the number of columns in the fetched MySQL row, i.e. schema/metadata column list is wider than the actual row returned by the MySQL client.

Common situations: MySQL CDC snapshot where the table was altered between metadata fetch and row read (column count mismatch); mismatch between the declared RW column list and the binlog/row event column set; replication protocol desync after schema change.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/12d966f89e3f72c8. Report an issue: GitHub.