SeaQL/sea-orm · error

Failed to get integer

Error message

Failed to get integer

What it means

Panic while converting a Postgres row to ProxyRow: a column matched as INT/SERIAL/INT4 is decoded with `row.try_get::<i32>(ordinal).expect("Failed to get integer")`. `try_get` fails when the value is NULL (scalar i32 target rejects NULL with `UnexpectedNullError`) or the runtime value cannot decode as i32 despite the reported type name (e.g. a DOMAIN over integer or a shadowed type).

Source

Thrown at sea-orm-sync/src/driver/sqlx_postgres.rs:478

                            row.try_get(c.ordinal())
                                .expect("Failed to get small integer"),
                        ),
                        #[cfg(feature = "postgres-array")]
                        "SMALLINT[]" | "SMALLSERIAL[]" | "INT2[]" => Value::Array(
                            sea_query::ArrayType::SmallInt,
                            row.try_get::<Option<Vec<i16>>, _>(c.ordinal())
                                .expect("Failed to get small integer array")
                                .map(|vals: Vec<i16>| {
                                    Box::new(
                                        vals.into_iter()
                                            .map(|val| Value::SmallInt(Some(val)))
                                            .collect(),
                                    )
                                }),
                        ),

                        "INT" | "SERIAL" | "INT4" => {
                            Value::Int(row.try_get(c.ordinal()).expect("Failed to get integer"))
                        }
                        #[cfg(feature = "postgres-array")]
                        "INT[]" | "SERIAL[]" | "INT4[]" => Value::Array(
                            sea_query::ArrayType::Int,
                            row.try_get::<Option<Vec<i32>>, _>(c.ordinal())
                                .expect("Failed to get integer array")
                                .map(|vals: Vec<i32>| {
                                    Box::new(
                                        vals.into_iter().map(|val| Value::Int(Some(val))).collect(),
                                    )
                                }),
                        ),

                        "BIGINT" | "BIGSERIAL" | "INT8" => Value::BigInt(
                            row.try_get(c.ordinal()).expect("Failed to get big integer"),
                        ),
                        #[cfg(feature = "postgres-array")]
                        "BIGINT[]" | "BIGSERIAL[]" | "INT8[]" => Value::Array(

View on GitHub (pinned to e29bcd1b41)

Solutions

  1. Decode as Option: `row.try_get::<Option<i32>, _>(c.ordinal())` and map to `Value::Int(opt)`.
  2. COALESCE the column in SQL when NULL should become a default (0).
  3. ALTER TABLE ... SET NOT NULL (with DEFAULT) on columns guaranteed non-null.
  4. Cast away DOMAINs (`col::integer`) so the sqlx decode target matches i32.
  5. Inspect `pg_typeof(col)` / search_path for shadowed types and fix schema or the match arm.

Example fix

// before (panics on NULL)
"INT" | "SERIAL" | "INT4" => Value::Int(row.try_get(c.ordinal()).expect("Failed to get integer")),
// after (NULL-safe)
"INT" | "SERIAL" | "INT4" => Value::Int(
    row.try_get::<Option<i32>, _>(c.ordinal())
        .expect("Failed to get integer")
        .unwrap_or_default(),
),
Defensive patterns

Strategy: try-catch

Validate before calling

// SELECT column_name, is_nullable, data_type FROM information_schema.columns
//   WHERE table_name = 't' AND data_type = 'integer';
let type_name = col.type_info().name().to_string();
debug_assert!(matches!(type_name.as_str(), "INT" | "SERIAL" | "INT4"));

Type guard

fn as_i32(row: &ProxyRow, col: &str) -> Option<i32> {
    match row.value(col) {
        Some(Value::Int(v)) => v,
        _ => None,
    }
}

Try / catch

let result = std::panic::catch_unwind(|| proxy_row_get_i32(&row, "parent_id"));
match result {
    Ok(v) => v,
    Err(_) => treat_as_null_or_default(), // nullable FK: treat as None
}

Prevention

When it happens

Trigger: Reading a nullable int4 column (including LEFT JOIN misses or nullable view columns) through ProxyRow; querying a Postgres DOMAIN over integer whose decode target doesn't match; serial columns in a result where NULL slipped in via an outer join.

Common situations: Nullable foreign-key columns read directly through raw/ProxyRow queries; views exposing nullable projections of NOT NULL base columns; DOMAIN types over integer used for enums; search_path shadowing of builtin types.

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/af6b365abdd5b70c. Report an issue: GitHub.