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
- Check each argument is a list/sequence before calling, e.g. wrap scalars in a list: `zip_strict([x], my_list)`
- Verify the source variables are defined: render them with `log()` or `{{ debug() }}` before the zip
- 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
- Wrap scalars in [ ] before zipping
- Use var('name', default=[]) to guarantee lists
- Check upstream queries return sequences, not scalars
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
- argument 'name' to has_var() has incompatible type; value…
- argument 'name' to var() has incompatible type; value is…
- local_md5's argument must be a string
- adapter not found in context
- adapter should be configured for the parse phase
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)