dbt-labs/dbt-core · error · minijinja::Error::InvalidOperation
agate_table must be an AgateTable
Error message
agate_table must be an AgateTable
What it means
`convert_type` converts an AgateTable column's agate type to the adapter's SQL data type. It requires the first argument (`agate_table`) to downcast to an AgateTable; this error is thrown when it cannot. The library enforces the type so it can index into the table's column type list with `col_idx`.
Source
Thrown at crates/dbt-adapter/src/adapter/mod.rs:741
/// https://github.com/dbt-labs/dbt-adapters/blob/main/dbt-adapters/src/dbt/adapters/base/impl.py#L1221
///
/// ```python
/// def convert_type(
/// cls,
/// agate_table: "agate.Table",
/// col_idx: int
/// ) -> Optional[str]
/// ```
#[tracing::instrument(skip_all, level = "trace")]
pub fn convert_type(&self, state: &State, args: &[Value]) -> Result<Value, minijinja::Error> {
match &self.inner {
Typed { adapter, .. } => {
let iter = ArgsIter::new("convert_type", &["agate_table", "col_idx"], args);
let table = iter
.next_arg::<&Value>()?
.downcast_object::<AgateTable>()
.ok_or_else(|| {
minijinja::Error::new(
minijinja::ErrorKind::InvalidOperation,
"agate_table must be an AgateTable",
)
})?;
let col_idx = iter.next_arg::<i64>()?;
iter.finish()?;
let result = adapter.convert_type(state, table, col_idx)?;
Ok(Value::from(result))
}
Parse(_) => Ok(empty_string_value()),
}
}
/// Render raw model constraints.
///
/// https://github.com/dbt-labs/dbt-adapters/blob/main/dbt-adapters/src/dbt/adapters/base/impl.py#L1891
///View on GitHub (pinned to 0267ce9170)
Solutions
- Ensure the caller wraps results in an AgateTable via load_agate_table() before invoking convert_type
- Check that the table object, not a column or a row, is being passed as the first argument
- If calling from Rust, pass Value::from_object(AgateTable) rather than Value::from_serialize
Example fix
// before adapter.convert_type(raw_result, 0) // after adapter.convert_type(load_agate_table(raw_result), 0)
Defensive patterns
Strategy: type-guard
Validate before calling
# python guard
if not isinstance(agate_table, agate.Table):
raise TypeError('convert_type requires an agate.Table, got ' + type(agate_table).__name__) Type guard
fn is_agate_table(v: &Value) -> bool {
v.downcast_object::<AgateTable>().is_some()
} Try / catch
match value.downcast_object::<AgateTable>() {
Some(table) => convert(table, col_idx),
None => Err(minijinja::Error::new(minijinja::ErrorKind::InvalidOperation, "agate_table must be an AgateTable")),
} Prevention
- Always pass the loaded agate table (load_agate_table result), not raw results
- Verify the first argument is the table object, not a column or row
- Write a smoke test for custom type-conversion macros
When it happens
Trigger: Calling convert_type(agate_table, col_idx) with a value that is not an AgateTable — e.g. passing the table as a serialized dict, a list of columns, or forgetting that the macro wrapper should pass the loaded agate table.
Common situations: Custom type-conversion macros (type_code / convert_type hooks) receive raw query results from an overridden load_dataframe or a custom materialization passes the wrong variable.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- grants_table must be an AgateTable
- show_tables_results must contain AgateTables
- ColumnsAsTuple::count_occurrences_of
- ColumnsAsTuple::index_of
- column_distinct
AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07).
Data as JSON: /api/errors/2917c695ce9996a8.
Report an issue: GitHub.