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
- Re-fetch the table schema so column metadata matches the current MySQL table, then retry the snapshot.
- Verify the index passed to the macro comes from iterating the same column list used to read the row.
- 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
- Keep the column metadata list and the row read loop derived from the same schema object.
- Freeze DDL on the MySQL source during snapshot/backfill.
- Log row width vs expected column count once per batch to catch drift early.
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
- received a DDL message, please set `canal.instance.filter.qu
- failed to deserialize MySQL value into rust value
- failed to deserialize MySQL value into rw value
- invalid value for boolean: {:?}
- unsupported data type: {}, set to null
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/12d966f89e3f72c8.
Report an issue: GitHub.