SeaQL/sea-orm · error

Failed to get small integer

Error message

Failed to get small integer

What it means

Panic while converting a Postgres row to ProxyRow: a column reporting type name `"CHAR"` (the internal 1-byte `"char"` type) is decoded with `row.try_get::<i8>(ordinal).expect("Failed to get small integer")`. `try_get` fails when the value is NULL (scalar target cannot be NULL) or the value is not decodable as `i8` — e.g. the column is actually `character(1)` (char/varchar), which sqlx reports differently but which developers often confuse with `"CHAR"`.

Source

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

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

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

                        "SMALLINT" | "SMALLSERIAL" | "INT2" => Value::SmallInt(
                            row.try_get(c.ordinal())
                                .expect("Failed to get small integer"),

View on GitHub (pinned to e29bcd1b41)

Solutions

  1. Decode as Option: `row.try_get::<Option<i8>, _>(c.ordinal())` and map to `Value::TinyInt(opt)` so NULL does not panic.
  2. If the column is really `character(1)`, decode as `Option<String>`/`char` and convert, or fix the type-name match to target the correct branch.
  3. COALESCE the column in SQL (`COALESCE(col, 0)`) if NULL should be 0.
  4. ALTER the column to SET NOT NULL when the model guarantees a value.
  5. Confirm the actual type with `SELECT pg_typeof(col)`; adjust the match arm or the query cast (`col::"char"`).

Example fix

// before (panics on NULL)
"\"CHAR\"" => Value::TinyInt(row.try_get(c.ordinal()).expect("Failed to get small integer")),
// after (NULL-safe)
"\"CHAR\"" => Value::TinyInt(
    row.try_get::<Option<i8>, _>(c.ordinal())
        .expect("Failed to get small integer")
        .unwrap_or_default(),
),
Defensive patterns

Strategy: try-catch

Validate before calling

// SELECT data_type, is_nullable FROM information_schema.columns
//   WHERE table_name = 't' AND udt_name = 'char'; -- distinguish "char" from bpchar
let type_name = col.type_info().name().to_string();
debug_assert_eq!(type_name, "\"CHAR\"", "unexpected column type: {}", type_name);

Type guard

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

Try / catch

let result = std::panic::catch_unwind(|| proxy_row_get_i8(&row, "cat_flag"));
match result {
    Ok(v) => v,
    Err(_) => treat_as_null_or_default(), // log actual column type for diagnosis
}

Prevention

When it happens

Trigger: Reading a nullable `"char"` column through ProxyRow so `try_get::<i8>` hits `UnexpectedNullError`; or querying a `char(1)`/`bpchar` column whose type-info match lands on the `"CHAR"` branch but whose Rust decode target i8 does not fit; or a DOMAIN over `"char"`.

Common situations: Reading Postgres catalog columns (many system catalogs use nullable `"char"` pseudo-booleans like `proisagg`-style flags); confusing SQL `CHAR(1)` with Postgres internal `"char"`; schema where a single-char column was created as varchar but matched by an alias.

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