SeaQL/sea-orm · error

Failed to get string

Error message

Failed to get string

What it means

Panic when sea-orm-sync's SQLite driver cannot decode a "TEXT" column into Option<String> while building a ProxyRow. Although the target is an Option, sqlx still fails when the underlying storage class is not text (e.g. BLOB or numeric) under a TEXT-typed column, thanks to SQLite's dynamic typing.

Source

Thrown at sea-orm-sync/src/driver/sqlx_sqlite.rs:432

                        "BOOLEAN" => {
                            Value::Bool(row.try_get(c.ordinal()).expect("Failed to get boolean"))
                        }

                        "INTEGER" => {
                            Value::Int(row.try_get(c.ordinal()).expect("Failed to get integer"))
                        }

                        "BIGINT" | "INT8" => Value::BigInt(
                            row.try_get(c.ordinal()).expect("Failed to get big integer"),
                        ),

                        "REAL" => {
                            Value::Double(row.try_get(c.ordinal()).expect("Failed to get double"))
                        }

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

                        "BLOB" => Value::Bytes(
                            row.try_get::<Option<Vec<u8>>, _>(c.ordinal())
                                .expect("Failed to get bytes")
                                .map(Box::new),
                        ),

                        #[cfg(feature = "with-chrono")]
                        "DATETIME" => {
                            use chrono::{DateTime, Utc};

                            Value::ChronoDateTimeUtc(
                                row.try_get::<Option<DateTime<Utc>>, _>(c.ordinal())
                                    .expect("Failed to get timestamp")
                                    .map(Box::new),
                            )

View on GitHub (pinned to e29bcd1b41)

Solutions

  1. Normalize storage: UPDATE t SET col = CAST(col AS TEXT) WHERE typeof(col) != 'text'
  2. Verify with SELECT typeof(col), count(*) GROUP BY typeof(col) to find offending rows
  3. Insert data through typed entity models so SQLite affinity converts values to text
  4. Propagate the sqlx error with column context instead of .expect and fall back to reading bytes

Example fix

// before
"TEXT" => Value::String(
    row.try_get::<Option<String>, _>(c.ordinal())
        .expect("Failed to get string")
        .map(Box::new),
),
// after
"TEXT" => Value::String(
    row.try_get::<Option<String>, _>(c.ordinal())
        .unwrap_or_else(|e| panic!("Failed to get string for col {}: {}", c.name(), e))
        .map(Box::new),
)   // better: fall back to CAST(col AS TEXT) at query time
Defensive patterns

Strategy: validation

Validate before calling

// Verify TEXT columns hold text storage class:
// SELECT typeof(col), count(*) FROM t GROUP BY typeof(col);

Type guard

fn is_text_storage(type_of: &str) -> bool { type_of == "text" }

Try / catch

let row = std::panic::catch_unwind(|| proxy_query(db)).unwrap_or_else(|_| fallback_row());

Prevention

When it happens

Trigger: Reading a TEXT column whose stored value has storage class BLOB or INTEGER/REAL that sqlx won't decode into Option<String>, or other sqlx-level decode errors on the column.

Common situations: Strings inserted as BLOBs (e.g. via bytes buffers in another driver); columns declared TEXT but holding numeric data inserted via raw SQL; corruption or mixed-type data imported from other databases.

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