dbt-labs/dbt-core · error · InvalidOperation

zip_strict requires all arguments to be iterable

Error message

zip_strict requires all arguments to be iterable

What it means

`zip_strict` requires every positional argument to be iterable; this error is raised when any argument fails `try_iter()`. Non-iterable values such as strings used as scalars, numbers, booleans, dicts (in some renderings), or undefined values cannot be zipped, so the function fails fast with this message instead of producing broken output.

Solutions

  1. Check each argument is a list/sequence before calling, e.g. wrap scalars in a list: `zip_strict([x], my_list)`
  2. Verify the source variables are defined: render them with `log()` or `{{ debug() }}` before the zip
  3. Replace a scalar count with an iterable: `range(n)` instead of `n`

Example fix

// before
{% set pairs = zip_strict(n, names) %}
// after
{% set pairs = zip_strict(range(n), names) %}
Defensive patterns

Strategy: type-guard

Validate before calling

{% if list1 is not iterable or list2 is not iterable %}{{ log('non-iterable input to zip_strict') }}{% endif %}

Type guard

{% macro as_list(v) %}{% if v is iterable and v is not string %}{{ v }}{% else %}{{ [v] }}{% endif %}{% endmacro %}

Try / catch

// Jinja has no try/catch; validate before calling:
{% if x is iterable %}{% set pairs = zip_strict(x, y) %}{% else %}{% set pairs = [] %}{% endif %}

Prevention

When it happens

Trigger: Calling `zip_strict(5, list)` or `zip_strict(dict, list)` — any argument whose `Value::try_iter()` returns Err, typically a scalar number, boolean, or undefined value passed where a list was expected.

Common situations: A variable intended to be a list is undefined because a config key was missing, a model returned a scalar instead of a column list, or an integer count was passed instead of a `range()` sequence.

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/6a3be3f2e9f5ae12. Report an issue: GitHub.

Appendix: source

Thrown at crates/dbt-jinja-utils/src/functions/base.rs:898

/// {% set list2 = ['a', 'b', 'c'] %}
/// {% set pairs = zip_strict(list1, list2) %}
/// -- Returns [(1, 'a'), (2, 'b'), (3, 'c')] or fails if inputs aren't iterable or lengths differ
/// ```
pub fn zip_strict_fn() -> impl Fn(&[Value], Kwargs) -> Result<Value, Error> {
    move |args: &[Value], _kwargs: Kwargs| -> Result<Value, Error> {
        if args.len() < 2 {
            return Err(Error::new(
                ErrorKind::InvalidOperation,
                "zip_strict requires two or more iterable arguments",
            ));
        }

        let mut iterators: Vec<Vec<Value>> = Vec::new();
        for arg in args {
            match arg.try_iter() {
                Ok(iter) => iterators.push(iter.collect()),
                Err(_) => {
                    return Err(Error::new(
                        ErrorKind::InvalidOperation,
                        "zip_strict requires all arguments to be iterable",
                    ));
                }
            }
        }

        // Find shortest length (Python's zip behavior)
        let min_len = iterators.iter().map(|v| v.len()).min().unwrap_or(0);

        let mut zipped = Vec::new();
        for i in 0..min_len {
            let tuple: Vec<Value> = iterators.iter().map(|iter| iter[i].clone()).collect();
            zipped.push(Value::from(tuple));
        }

        Ok(Value::from_iter(zipped))
    }

View on GitHub (pinned to 0267ce9170)