SeaQL/sea-orm · error

Failed to get year

Error message

Failed to get year

What it means

ProxyRow panicked while converting a MySQL YEAR column with `with-chrono`: the driver attempted `row.try_get::<Option<chrono::NaiveDate>, _>(c.ordinal())` and `.expect("Failed to get year")` panicked because sqlx returned a decode error — the YEAR wire value (often a u16/integer) is not decodable as NaiveDate by sqlx's MySQL decoder.

Source

Thrown at src/driver/sqlx_mysql.rs:506

                        ),

                        #[cfg(feature = "with-chrono")]
                        "DATETIME" => Value::ChronoDateTime(
                            row.try_get::<Option<chrono::NaiveDateTime>, _>(c.ordinal())
                                .expect("Failed to get datetime")
                                .map(Box::new),
                        ),
                        #[cfg(all(feature = "with-time", not(feature = "with-chrono")))]
                        "DATETIME" => Value::TimeDateTime(
                            row.try_get::<Option<time::PrimitiveDateTime>, _>(c.ordinal())
                                .expect("Failed to get datetime")
                                .map(Box::new),
                        ),

                        #[cfg(feature = "with-chrono")]
                        "YEAR" => Value::ChronoDate(
                            row.try_get::<Option<chrono::NaiveDate>, _>(c.ordinal())
                                .expect("Failed to get year")
                                .map(Box::new),
                        ),
                        #[cfg(all(feature = "with-time", not(feature = "with-chrono")))]
                        "YEAR" => Value::TimeDate(
                            row.try_get::<Option<time::Date>, _>(c.ordinal())
                                .expect("Failed to get year")
                                .map(Box::new),
                        ),

                        "ENUM" | "SET" | "GEOMETRY" => Value::String(
                            row.try_get::<Option<String>, _>(c.ordinal())
                                .expect("Failed to get serialized string")
                                .map(Box::new),
                        ),

                        #[cfg(feature = "with-bigdecimal")]
                        "DECIMAL" => Value::BigDecimal(
                            row.try_get::<Option<bigdecimal::BigDecimal>, _>(c.ordinal())

View on GitHub (pinned to e29bcd1b41)

Solutions

  1. Avoid decoding YEAR as a date: cast in SQL — SELECT CAST(y AS CHAR) AS y or SELECT y + 0 AS y — and read it as a String/integer.
  2. Better: migrate the column type from YEAR to SMALLINT in the schema so it maps to an integer Value.
  3. Select the year wrapped as a DATE ( MAKEDATE(y, 1) ) only if you genuinely need a NaiveDate.
  4. Check the sea-orm/sqlx versions: newer sqlx versions handle YEAR differently; upgrade or downgrade in lockstep.
  5. If only the proxy/raw path breaks, prefer generated entity queries (typed Decodable) over ProxyRow for YEAR columns.

Example fix

// before — YEAR column decoded as NaiveDate, panics
SELECT founded_year FROM companies

// after — read YEAR as an integer
SELECT founded_year + 0 AS founded_year FROM companies
Defensive patterns

Strategy: validation

Validate before calling

// Check the column type before treating YEAR as a date
let ty: String = db.query_one(&Statement::from_string(
    "SELECT COLUMN_TYPE AS t FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'companies' AND COLUMN_NAME = 'founded_year'",
    DbBackend::MySql)).await?.unwrap().try_get("t")?;
if ty.starts_with("year") { /* decode as i32/String, never NaiveDate */ }

Type guard

fn is_year_column(col_type: &str) -> bool {
    col_type.eq_ignore_ascii_case("year") || col_type.starts_with("year(")
}

Try / catch

// Prefer avoiding the panic entirely by mapping YEAR to integers in SQL
let year: Option<i32> = row.try_get(ordinal)  // via `founded_year + 0`
    .map_err(|_| YearDecodeFallback)?;

Prevention

When it happens

Trigger: Selecting a YEAR-typed column: the MySQL wire type for YEAR is an integer year, which frequently cannot be decoded into NaiveDate, especially when the value is fetched as numeric (YEAR columns read via binary protocol return u16). Any YEAR column read through ProxyRow with chrono enabled can hit this.

Common situations: Schemas using YEAR(4) columns; proxy/raw-SQL paths that bypass generated entity typing; version changes in sqlx changing how YEAR is delivered; mixing proxy decoding with typed column expectations.

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 SeaQL/sea-orm@e29bcd1b41 (2026-09-10). Data as JSON: /api/errors/4d6e2c44f71fbcb1. Report an issue: GitHub.