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

existing_columns must contain Column objects

Error message

existing_columns must contain Column objects

What it means

After successful iteration, every element of existing_columns must be a Column object; the code downcasts each Value to Column and raises InvalidOperation 'existing_columns must contain Column objects' on the first element that is not one. The adapter needs typed Column objects to extract names/types and build constraints.

Source

Thrown at crates/dbt-adapter/src/adapter/adapter_impl.rs:4659

        if self.adapter_type() != Databricks && self.adapter_type() != Spark {
            return Err(minijinja::Error::new(
                minijinja::ErrorKind::InvalidOperation,
                "parse_columns_and_constraints is only available for Databricks/Spark adapter",
            ));
        }

        let columns: Vec<Column> = existing_columns
            .try_iter()
            .map_err(|e| {
                minijinja::Error::new(
                    minijinja::ErrorKind::InvalidOperation,
                    format!("existing_columns must be iterable: {e}"),
                )
            })?
            .map(|v| {
                v.downcast_object_ref::<Column>().cloned().ok_or_else(|| {
                    minijinja::Error::new(
                        minijinja::ErrorKind::InvalidOperation,
                        "existing_columns must contain Column objects",
                    )
                })
            })
            .collect::<Result<Vec<_>, _>>()?;

        let model_columns_map: BTreeMap<String, DbtColumn> =
            minijinja_value_to_typed_struct(model_columns.clone()).map_err(|e| {
                minijinja::Error::new(
                    minijinja::ErrorKind::SerdeDeserializeError,
                    format!("model_columns: {e}"),
                )
            })?;

        let model_constraints_vec: Vec<ModelConstraint> =
            minijinja_value_to_typed_struct(model_constraints.clone()).map_err(|e| {
                minijinja::Error::new(

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Build elements with the Column type (e.g. api.Column / adapter column class) instead of plain dicts.
  2. Convert each dict into a Column before calling: Column(name=..., dtype=...).
  3. Use get_columns_in_relation output directly, which already contains Column objects.
  4. Filter out any non-Column entries (e.g. stray strings) from the list before the call.

Example fix

// before
cols = [{"name": "id", "dtype": "int"}]
adapter.parse_columns_and_constraints(cols, model_columns, name)

// after
cols = [api.Column("id", "int")]
adapter.parse_columns_and_constraints(cols, model_columns, name)
Defensive patterns

Strategy: validation

Validate before calling

def all_columns_are_objects(v):
    return all(getattr(i, 'name', None) is not None or (isinstance(i, dict) and 'name' in i) for i in v)

Type guard

def is_column_obj(x):
    return hasattr(x, 'name') and hasattr(x, 'dtype')

Try / catch

try:
    parsed = adapter.parse_columns_and_constraints(existing, model_columns, name)
except Exception as e:
    if 'must contain Column objects' in str(e):
        raise ValueError('Convert dicts to api.Column objects before calling parse_columns_and_constraints') from e
    raise

Prevention

When it happens

Trigger: existing_columns is iterable but contains plain dicts, strings, or other Jinja values instead of Column objects — e.g. a hand-built list of {name, type} dicts.

Common situations: Constructing columns manually in a macro as dicts instead of using api.Column / the adapter's Column type; deserializing columns from JSON into plain maps; mixing Column objects with raw values in one list.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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