dbt-labs/dbt-core · error
Failed to downcast response
Error message
Failed to downcast response
What it means
This `TryFrom<Value>` conversion for `AdapterResponse` succeeds only if the value is an `AdapterResponse` object or a plain string (treated as the response message). Any other minijinja Value falls through to this CannotDeserialize error, meaning the value could not be downcast to an adapter response.
Solutions
- Return an `AdapterResponse` object from the adapter callback, or a plain string message.
- If you control the caller, inspect the value's actual type (log its kind) and normalize it before conversion.
- Check for adapter/plugin version mismatches that changed the response object type expected by this conversion.
Example fix
// before
Ok(Value::from_object(serde_json::json!({"message": "OK"})))
// after
Ok(Value::from_object(AdapterResponse::new().with_message("OK"))) Defensive patterns
Strategy: type-guard
Validate before calling
// Rust
fn is_adapter_response(v: &Value) -> bool {
v.downcast_object::<AdapterResponse>().is_some() || v.as_str().is_some()
} Type guard
fn as_adapter_response(v: Value) -> Option<AdapterResponse> {
v.downcast_object::<AdapterResponse>().map(|o| (*o).clone())
.or_else(|| v.as_str().map(|m| AdapterResponse::new().with_message(m.to_string())))
} Try / catch
match AdapterResponse::try_from(value) {
Ok(resp) => ...,
Err(e) => log::warn!("response not downcastable: {e}"),
} Prevention
- Always return AdapterResponse (or a string) from adapter execute callbacks.
- Avoid wrapping responses in dicts or intermediate objects in custom materializations.
- Keep custom adapters aligned with the AdapterResponse API of the installed adapter crate.
When it happens
Trigger: Converting a Jinja `Value` into `AdapterResponse` where the value is neither an AdapterResponse object nor a string — e.g. a dict returned by a custom adapter callback, a number, or a serialized response map.
Common situations: Custom adapter/materialization code returning a dict or non-standard response from an `execute`/statement callback; plugin code that wraps the response in another object type after an adapter upgrade.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- adapter.add_time_ingestion_partition_column failed on…
- agate_table
- agate_table must be an AgateTable
- compute_external_path: Failed to deserialize config
- compute_external_path: Failed to deserialize…
AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07).
Data as JSON: /api/errors/12a8a3604f7e0886.
Report an issue: GitHub.
Appendix: source
Thrown at crates/dbt-adapter/src/response.rs:370
let is_known = key.as_str().is_some_and(|s| KNOWN_KEYS.contains(&s));
if !is_known {
keys.push(key.clone());
}
}
Enumerator::Iter(Box::new(keys.into_iter()))
}
}
impl TryFrom<Value> for AdapterResponse {
type Error = minijinja::Error;
fn try_from(value: Value) -> Result<Self, Self::Error> {
if let Some(response) = value.downcast_object::<AdapterResponse>() {
Ok((*response).clone())
} else if let Some(message_str) = value.as_str() {
Ok(AdapterResponse::new().with_message(message_str))
} else {
Err(minijinja::Error::new(
minijinja::ErrorKind::CannotDeserialize,
"Failed to downcast response",
))
}
}
}
/// load_result response object
#[derive(Debug)]
pub struct ResultObject {
pub response: AdapterResponse,
pub table: Option<AgateTable>,
#[allow(unused)]
pub data: Option<Value>,
}
impl ResultObject {
pub fn new(response: AdapterResponse, table: Option<AgateTable>) -> Self {View on GitHub (pinned to 0267ce9170)