transact-rs/sqlx · error
downcast to wrong DatabaseError type; original error: {e}
Error message
downcast to wrong DatabaseError type; original error: {e} What it means
DatabaseError::downcast() (boxed variant, sqlx-core/src/error.rs:299) panics when `Box<dyn DatabaseError>` cannot be converted into the requested concrete error type E. It is the owning counterpart of downcast_ref; a failed downcast means the error came from a different database backend than assumed. The panic message includes the original error for diagnosis.
Source
Thrown at sqlx-core/src/error.rs:299
/// 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]
pub fn try_downcast_ref<E: DatabaseError>(&self) -> Option<&E> {
self.as_error().downcast_ref()
}
/// Downcast this generic database error to a specific database error type.
#[inline]
pub fn try_downcast<E: DatabaseError>(self: Box<Self>) -> Result<Box<E>, Box<Self>> {
if self.as_error().is::<E>() {
Ok(self.into_error().downcast().unwrap())
} else {
Err(self)
}
}View on GitHub (pinned to 03af8bcc57)
Solutions
- Switch to `try_downcast::<E>()` which returns Option/Result and handle the mismatch gracefully.
- Track the backend alongside stored errors (enum wrapper) so the correct concrete type is requested.
- Restructure to keep backend-specific error handling adjacent to the backend-specific pool that produced the error.
Example fix
// before
let mysql_err = boxed_err.downcast::<MySqlDatabaseError>();
// after
match boxed_err.try_downcast::<MySqlDatabaseError>() {
Some(e) => handle_mysql(e),
None => log::warn!("non-MySQL db error: {boxed_err}"),
} Defensive patterns
Strategy: type-guard
Type guard
fn try_owned_downcast(db_err: Box<dyn sqlx::error::DatabaseError>) -> Option<Box<sqlx::mysql::MySqlDatabaseError>> {
db_err.try_downcast::<sqlx::mysql::MySqlDatabaseError>()
} Try / catch
// Use the fallible variant:
let Ok(mysql_err) = boxed_err.try_downcast::<MySqlDatabaseError>() else {
return handle_other(boxed_err);
}; Prevention
- Never store bare Box<dyn DatabaseError> when the concrete type will be needed later — store an enum with the backend tag
- Default to try_downcast in shared/retry/middleware layers
- Add tests covering error paths for every database backend you support
When it happens
Trigger: `err.downcast::<MySqlDatabaseError>()` on a boxed error originating from Postgres/SQLite, typically inside a shared helper that takes `Box<dyn DatabaseError>` or stores errors from mixed pools, then unwraps to one specific backend type.
Common situations: Middleware or retry layers that persist and later re-downcast database errors assuming a fixed backend; test suites where a different database feature is enabled than in production code paths; migrating an app from one database to another without updating downcast targets.
Related errors
- downcast to wrong DatabaseError type; original error: {self}
- invalid column index: {}
- unimplemented!()
- failed to close setup connection
- Could not fetch metadata
AI-assisted analysis of transact-rs/sqlx@03af8bcc57 (2026-09-03).
Data as JSON: /api/errors/cea0702da9fd6516.
Report an issue: GitHub.