transact-rs/sqlx · error

downcast to wrong DatabaseError type; original error: {self}

Error message

downcast to wrong DatabaseError type; original error: {self}

What it means

DatabaseError::downcast_ref() (sqlx-core/src/error.rs) panics when the caller asks for a concrete database error type E but the stored error is of a different backend's type. Unlike try_downcast_ref (which returns Option), this is the infallible variant meant for code that already knows the backend; mismatch indicates a logic bug, so sqlx panics and prints the original error.

Source

Thrown at sqlx-core/src/error.rs:286

    /// Returns whether the error kind is a violation of a check.
    fn is_check_violation(&self) -> bool {
        matches!(self.kind(), ErrorKind::CheckViolation)
    }
}

impl dyn DatabaseError {
    /// Downcast a reference to this generic database error to a specific
    /// database error type.
    ///
    /// # Panics
    ///
    /// Panics if the database error type is not `E`. This is a deliberate contrast from
    /// `Error::downcast_ref` which returns `Option<&E>`. In normal usage, you should know the
    /// specific error type. In other cases, use `try_downcast_ref`.
    pub fn downcast_ref<E: DatabaseError>(&self) -> &E {
        self.try_downcast_ref().unwrap_or_else(|| {
            panic!("downcast to wrong DatabaseError type; original error: {self}")
        })
    }

    /// Downcast this generic database error to a specific database error type.
    ///
    /// # Panics
    ///
    /// Panics if the database error type is not `E`. This is a deliberate contrast from
    /// `Error::downcast` which returns `Option<E>`. In normal usage, you should know the
    /// specific error type. In other cases, use `try_downcast`.
    pub fn downcast<E: DatabaseError>(self: Box<Self>) -> Box<E> {
        self.try_downcast()
            .unwrap_or_else(|e| panic!("downcast to wrong DatabaseError type; original error: {e}"))
    }

    /// Downcast a reference to this generic database error to a specific
    /// database error type.
    #[inline]

View on GitHub (pinned to 03af8bcc57)

Solutions

  1. Use `try_downcast_ref::<E>()` and handle the None case instead of the panicking variant.
  2. Check the backend before downcasting (e.g. keep separate error-handling paths per pool type, or match on the connection/pool type).
  3. If using AnyPool, downcast to `any::AnyDatabaseError` style handling or use backend-specific pools where you know the error type.

Example fix

// before
let pg_err = db_err.downcast_ref::<PgDatabaseError>();

// after
if let Some(pg_err) = db_err.try_downcast_ref::<PgDatabaseError>() {
    handle_pg(pg_err);
} else {
    handle_generic(db_err);
}
Defensive patterns

Strategy: type-guard

Type guard

fn as_pg_error(db_err: &dyn sqlx::error::DatabaseError) -> Option<&sqlx::postgres::PgDatabaseError> {
    db_err.try_downcast_ref::<sqlx::postgres::PgDatabaseError>()
}

Try / catch

// Downcasts are infallible-looking; use the Try variants instead of catching panics:
match db_err.try_downcast_ref::<PgDatabaseError>() {
    Some(e) => handle_pg(e),
    None => handle_generic(db_err),
}

Prevention

When it happens

Trigger: Calling `err.as_database_error().unwrap().downcast_ref::<PgDatabaseError>()` (or MySqlDatabaseError, SqliteError...) on an error produced by a different driver — e.g. handling errors from a multi-backend `AnyPool`, or a generic error-handling path shared across Postgres and MySQL pools.

Common situations: Code written for PgPool later reused with a SqlitePool or AnyPool; CI running against a different database than production; match arms that call downcast_ref without first checking which backend produced the error.

Related errors


AI-assisted analysis of transact-rs/sqlx@03af8bcc57 (2026-09-03). Data as JSON: /api/errors/933c772cf2972f05. Report an issue: GitHub.