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 columns

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Convert the grants query result to an AgateTable before calling (e.g. `load_agate_table()` in the macro's Python wrapper)
  2. Verify the macro signature order: grants_table must be the first positional arg bound to the agate table
  3. 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

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


AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07). Data as JSON: /api/errors/98e71804ac8a4d2b. Report an issue: GitHub.