risingwavelabs/risingwave · error
failed to deserialize MySQL value into rust value
Error message
failed to deserialize MySQL value into rust value
What it means
The `handle_data_type!` macro wraps MySQL client deserialization failures (`Some(Err(e))` from `take_opt`) with the context "failed to deserialize MySQL value into rust value" plus column name, index, and the target Rust type. This means the raw MySQL value could not be converted into the requested Rust type (e.g. malformed string for a numeric type, incompatible wire format).
Source
Thrown at src/connector/src/parser/mysql.rs:40
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!(
"column: {}, index: {}, rw_type: {}",
$name,View on GitHub (pinned to 6469eb736d)
Solutions
- Read the inner error and context (column, index, rust_type) to identify the offending column and verify its actual MySQL type.
- Update the source table or RW schema so the declared type matches the upstream column type, then re-create/restart the source.
- If it is data corruption in a specific row, repair or skip the row (e.g. fix the value upstream and resume the snapshot).
Defensive patterns
Strategy: try-catch
Try / catch
match decode_col(row, idx, name) {
Err(e) if e.to_string().contains("failed to deserialize MySQL value into rust value") => {
tracing::error!("mysql type mismatch: {e:#}"); // {:#} prints the full context chain
quarantine_row(row_id);
}
other => other?,
} Prevention
- Keep the RW-to-MySQL type mapping in sync with upstream DDL changes.
- Use DESCRIBE/INFORMATION_SCHEMA checks against the live table before starting CDC.
- Alert on any deserialization error; it usually means schema drift or data corruption.
When it happens
Trigger: `row.take_opt::<Option<T>, _>(i)` returns Err for column `$i` — the on-wire MySQL value's type does not match the Rust type parameter `$typ` the macro was instantiated with.
Common situations: Schema drift: column type changed upstream (e.g. INT to VARCHAR) so the typed read fails; charset/encoding issues on string columns read as numeric; connector type mapping table out of sync with the MySQL server version.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- failed to deserialize MySQL value into rw value
- invalid value for boolean: {:?}
- unexpected default value type for integer
- unexpected default value type for real
- received a DDL message, please set `canal.instance.filter.qu
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/11a09dbe0282825c.
Report an issue: GitHub.