SeaQL/sea-orm · error

Failed to get timetz

Error message

Failed to get timetz

What it means

ProxyRow panics while decoding a PostgreSQL TIMETZ (time with time zone) column: sqlx's `try_get` into `Option<chrono::NaiveTime>` (chrono path) or time::Time (with-time path) failed and `.expect` converts it to a panic. This means the column value cannot be represented in the expected time type — TIMETZ stores an offset-aware time and not all values decode cleanly, especially with unusual offsets or when the underlying column is not really timetz.

Source

Thrown at src/driver/sqlx_postgres.rs:887

                            feature = "postgres-array"
                        ))]
                        "TIMESTAMPTZ[]" => Value::Array(
                            sea_query::ArrayType::TimeDateTimeWithTimeZone,
                            row.try_get::<Option<Vec<time::OffsetDateTime>>, _>(c.ordinal())
                                .expect("Failed to get timestamptz array")
                                .map(|vals| {
                                    Box::new(
                                        vals.into_iter()
                                            .map(|val| Value::TimeDateTimeWithTimeZone(Some(val)))
                                            .collect(),
                                    )
                                }),
                        ),

                        #[cfg(feature = "with-chrono")]
                        "TIMETZ" => Value::ChronoTime(
                            row.try_get::<Option<chrono::NaiveTime>, _>(c.ordinal())
                                .expect("Failed to get timetz"),
                        ),
                        #[cfg(all(feature = "with-time", not(feature = "with-chrono")))]
                        "TIMETZ" => {
                            Value::TimeTime(row.try_get(c.ordinal()).expect("Failed to get timetz"))
                        }

                        #[cfg(all(feature = "with-chrono", feature = "postgres-array"))]
                        "TIMETZ[]" => Value::Array(
                            sea_query::ArrayType::ChronoTime,
                            row.try_get::<Option<Vec<chrono::NaiveTime>>, _>(c.ordinal())
                                .expect("Failed to get timetz array")
                                .map(|vals| {
                                    Box::new(
                                        vals.into_iter()
                                            .map(|val| Value::ChronoTime(Some(val)))
                                            .collect(),
                                    )
                                }),

View on GitHub (pinned to e29bcd1b41)

Solutions

  1. Confirm the true column type (`\d table`); if it is `time without time zone` or `interval`, cast in SQL to `col::timetz` or fix the schema.
  2. Normalize offsets: return `col AT TIME ZONE 'UTC'` so decode targets a well-behaved value, or change the column to `timestamptz` if the offset actually matters.
  3. Align feature flags (`with-chrono` vs `with-time`) so the matching decode arm handles your data.
  4. Update sea-orm/sqlx for decode fixes for edge-case timetz offsets.
  5. Workaround: select `col::text` and parse the time manually.

Example fix

// before: column is interval mislabeled as timetz in a view
"SELECT duration AS started FROM jobs"

// after
"SELECT (started)::timetz AS started FROM jobs"  // or fix the view to use timetz
Defensive patterns

Strategy: validation

Validate before calling

let dtype = sqlx::query_scalar::<_, String>(
    "SELECT data_type FROM information_schema.columns WHERE table_name=$1 AND column_name=$2"
).bind(table).bind(column).fetch_one(&db).await?;
if dtype != "time with time zone" { return Err(AppError::SchemaMismatch(format!("expected timetz, got {dtype}"))); }

Type guard

fn is_timetz(meta: &sea_orm::ColumnMeta) -> bool {
    meta.col_type.as_str().eq_ignore_ascii_case("TIMETZ")
}

Try / catch

let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
    entity::Entity::find().from_raw_sql(stmt).into_model::<M>().all(&db)
}));
let rows = res.map_err(|_| AppError::DecodeFailed("timetz decode panicked"))??;

Prevention

When it happens

Trigger: Fetching a TIMETZ column whose value cannot decode into NaiveTime/Time — columns actually declared time/timestamp/interval, domain types over timetz, raw-SQL expressions with mismatched metadata, or values with large UTC offsets that cannot round-trip into the target type.

Common situations: Legacy schemas where an `interval` column is labeled timetz in metadata; `timetz` values with offsets like `+13:59` that chrono/time reject on decode; raw selects casting `now()::timetz` through views; feature-flag mismatches so the wrong decode arm runs.

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