dbt-labs/dbt-core · error · minijinja::Error::InvalidOperation

svv_columns must be an AgateTable

Error message

svv_columns must be an AgateTable

What it means

The same catalog builder also requires the `svv_columns` argument to be a single AgateTable (the SVV_REDSHIFT_COLUMNS snapshot). This error is raised when downcasting that argument fails. The library enforces this so column metadata joins against the show-tables tables use the agate table API.

Source

Thrown at crates/dbt-adapter/src/adapter/mod.rs:571

                    &["show_tables_results", "svv_columns"],
                    args,
                );
                let show_tables_value = iter.next_arg::<&Value>()?;
                let mut show_tables_results: Vec<Arc<AgateTable>> = Vec::new();
                for table_value in show_tables_value.try_iter()? {
                    let table = table_value.downcast_object::<AgateTable>().ok_or_else(|| {
                        minijinja::Error::new(
                            minijinja::ErrorKind::InvalidOperation,
                            "show_tables_results must contain AgateTables",
                        )
                    })?;
                    show_tables_results.push(table);
                }
                let svv_columns = iter
                    .next_arg::<&Value>()?
                    .downcast_object::<AgateTable>()
                    .ok_or_else(|| {
                        minijinja::Error::new(
                            minijinja::ErrorKind::InvalidOperation,
                            "svv_columns must be an AgateTable",
                        )
                    })?;
                iter.finish()?;

                let catalog = adapter.build_catalog_from_show_tables_and_svv_columns(
                    &show_tables_results,
                    svv_columns,
                )?;
                Ok(Value::from_object(catalog))
            }
            // During parse phase queries don't execute, so there are no real tables to join.
            Parse(_) => Ok(Value::from_object(AgateTable::default())),
        }
    }

    /// Encloses identifier in the correct quotes for the adapter when escaping reserved column names etc.

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Convert the SVV columns result with load_agate_table() before calling the builder
  2. Verify argument order: show_tables_results first, then svv_columns
  3. If the SVV query returned nothing, pass an empty AgateTable rather than None or a dict

Example fix

// before
svv_columns = cursor.fetchall()
// after
svv_columns = load_agate_table({'column': [{'name': d[0], 'data_type': d[1]} for d in cursor.fetchall()]})
Defensive patterns

Strategy: type-guard

Validate before calling

# python guard before the call
if svv_columns is None or not hasattr(svv_columns, 'column_names'):
    svv_columns = load_agate_table(svv_rows)

Type guard

fn is_agate_table(v: &Value) -> bool {
    v.downcast_object::<AgateTable>().is_some()
}

Try / catch

match svv_value.downcast_object::<AgateTable>() {
    Some(t) => build_catalog(show_tables_results, t),
    None => Err(minijinja::Error::new(minijinja::ErrorKind::InvalidOperation, "svv_columns must be an AgateTable")),
}

Prevention

When it happens

Trigger: Passing anything other than an AgateTable as the `svv_columns` argument to build_catalog_from_show_tables_and_svv_columns — e.g. a dict, list of rows, or serialized JSON of SVV_REDSHIFT_COLUMNS.

Common situations: Custom Redshift catalog scripts feed SVV query output directly as Python dicts; or argument order was swapped so the wrong value lands in the svv_columns slot.

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/8d42ae81bad941c8. Report an issue: GitHub.