dbt-labs/dbt-core · error · minijinja::Error::InvalidOperation
grants_table must be an AgateTable
Error message
grants_table must be an AgateTable
What it means
`standardize_grants_dict` expects its `grants_table` argument to be a Minijinja Value wrapping an `AgateTable` object. The library throws this InvalidOperation error when the value cannot be downcast to AgateTable. This guards the Rust side against being handed arbitrary Jinja values (dicts, lists, strings) in place of an agate table of GRANT rows.
Source
Thrown at crates/dbt-adapter/src/adapter/mod.rs:514
/// def standardize_grants_dict(
/// self,
/// grants_table: "agate.Table"
/// ) -> dict
/// ```
#[tracing::instrument(skip_all, level = "trace")]
pub fn standardize_grants_dict(
&self,
_state: &State,
args: &[Value],
) -> Result<Value, minijinja::Error> {
match &self.inner {
Typed { adapter, .. } => {
let iter = ArgsIter::new("standardize_grants_dict", &["grants_table"], args);
let grants_table = iter
.next_arg::<&Value>()?
.downcast_object::<AgateTable>()
.ok_or_else(|| {
minijinja::Error::new(
minijinja::ErrorKind::InvalidOperation,
"grants_table must be an AgateTable",
)
})?;
Ok(Value::from_serialize(
&adapter.standardize_grants_dict(grants_table)?,
))
}
// This method is typically called after show grants SQL + run_query.
// During parse phase, run_query returns Undefined since queries don't execute,
// so we don't have an actual AgateTable. Return an empty grants dict to avoid
// downcast errors on Undefined values.
Parse(_) => Ok(Value::from(BTreeMap::<Value, Vec<Value>>::new())),
}
}
/// Build catalog from show tables and svv columnsView on GitHub (pinned to 0267ce9170)
Solutions
- Convert the grants query result to an AgateTable before calling (e.g. `load_agate_table()` in the macro's Python wrapper)
- Verify the macro signature order: grants_table must be the first positional arg bound to the agate table
- If writing a custom adapter, ensure the value passed is `Value::from_object(AgateTable)` not `Value::from_serialize(rows)`
Example fix
// before (caller) adapter.standardize_grants_dict(grants_dict) // after adapter.standardize_grants_dict(load_agate_table(grants_response))
Defensive patterns
Strategy: type-guard
Validate before calling
// python caller guard
if not isinstance(grants_table, agate.Table):
grants_table = load_agate_table(grants_table) Type guard
fn as_agate_table(v: &Value) -> Option<Arc<AgateTable>> {
v.downcast_object::<AgateTable>()
} Try / catch
match grants_value.downcast_object::<AgateTable>() {
Some(t) => standardize(t),
None => Err(Error::new(InvalidOperation, "grants_table must be an AgateTable; wrap results with load_agate_table()")),
} Prevention
- Always convert query results via load_agate_table() before passing to adapter grant functions
- Never pass serialized dicts/lists where adapter signatures expect AgateTable
- Keep custom grant macro signatures identical to the built-in ones
When it happens
Trigger: Calling the `standardize_grants_dict` adapter function from Jinja/Python with a `grants_table` argument that is a plain dict/list/string rather than an AgateTable — typically when hand-building the grants result instead of using `load_agate_table()`.
Common situations: Custom grant macros (get_grant.sql) return raw query results converted incorrectly, or a custom adapter override passes a serialized dict of grant rows where an AgateTable is expected.
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
- show_tables_results must contain AgateTables
- agate_table must be an AgateTable
- 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/98e71804ac8a4d2b.
Report an issue: GitHub.