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

  1. Read the inner error and context (column, index, rust_type) to identify the offending column and verify its actual MySQL type.
  2. Update the source table or RW schema so the declared type matches the upstream column type, then re-create/restart the source.
  3. 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

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


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