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

existing_columns must be iterable: {e}

Error message

existing_columns must be iterable: {e}

What it means

Within parse_columns_and_constraints, the existing_columns argument is iterated via try_iter(); if the Value is not an iterable sequence (e.g. a scalar, dict, or string), the failure is wrapped in an InvalidOperation error stating existing_columns must be iterable. The adapter requires a list-like structure of column objects.

Source

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

        model_columns: &Value,
        model_constraints: &Value,
        contract_enforced: bool,
        model_name: &str,
    ) -> Result<Value, minijinja::Error> {
        use crate::relation::databricks::typed_constraint;
        use std::collections::{BTreeMap, BTreeSet};

        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,

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Pass the output of get_columns_in_relation(relation) — a sequence of Column objects — as existing_columns.
  2. If you have a dict, convert it to a list of Column objects before calling.
  3. Check the error's inner message for what try_iter rejected and fix the value's type.
  4. Verify macro argument order wasn't swapped between existing_columns and model_columns.

Example fix

// before
cols = model.columns  # dict, not iterable sequence of Columns
adapter.parse_columns_and_constraints(cols, model_columns, name)

// after
cols = adapter.get_columns_in_relation(target_relation)
adapter.parse_columns_and_constraints(cols, model_columns, name)
Defensive patterns

Strategy: validation

Validate before calling

def is_iterable_columns(v):
    try:
        items = list(v)
    except TypeError:
        return False
    return all(isinstance(i, dict) and 'name' in i for i in items)

Type guard

def as_iterable(v):
    try:
        return list(v)
    except TypeError:
        return None

Try / catch

try:
    parsed = adapter.parse_columns_and_constraints(existing, model_columns, name)
except Exception as e:
    if 'existing_columns must be iterable' in str(e):
        existing = adapter.get_columns_in_relation(target_relation)
        parsed = adapter.parse_columns_and_constraints(existing, model_columns, name)
    else:
        raise

Prevention

When it happens

Trigger: Calling parse_columns_and_constraints with existing_columns as a non-iterable Value: a single object, a dict mapping names to columns, a string, or None.

Common situations: Passing a column dict from model.config instead of the relation's column list; forgetting that existing_columns comes from get_columns_in_relation (a sequence); macro refactors that changed the argument shape.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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