dbt-labs/dbt-core · error · InvalidOperation
{message_if_exception}
Error message
{message_if_exception} What it means
try_or_compiler_error(message_if_exception, fn, ...) invokes a wrapped dbt Jinja function and, if the call fails, raises an InvalidOperation error whose message is the caller-supplied message_if_exception string. The thrown message is dynamic — it is whatever you passed as the first argument — so this error text reflects your own template's message, not a fixed library string.
Solutions
- Inspect the wrapped function's inputs — the wrapper hides the underlying error, so validate arguments before the call
- Pass a descriptive message_if_exception so the thrown CompilationError pinpoints the model and inputs
- Temporarily replace try_or_compiler_error with a direct call to see the real underlying error
- Fix the inner call so it succeeds rather than relying on the guard
Example fix
// before
{% set x = try_or_compiler_error('bad input', zip(1, 2)) %}
// after
{% if 1 is sequence and 2 is sequence %}
{% set x = try_or_compiler_error('zip args must be lists: ' ~ this, zip([1], [2])) %}
{% endif %} Defensive patterns
Strategy: try-catch
Try / catch
{# try_or_compiler_error swallows the inner error; validate inputs first #}
{% if a is sequence and b is sequence %}
{% set x = try_or_compiler_error('zip failed in ' ~ this, zip(a, b)) %}
{% else %}
{{ exceptions.raise_compiler_error('zip inputs must be lists in ' ~ this) }}
{% endif %} Prevention
- Validate the wrapped function's arguments before the call — the wrapper hides root causes
- Include model context ('this') in message_if_exception for debuggability
- Temporarily unwrap the call to see the real error during debugging
- Keep message_if_exception descriptive and specific
When it happens
Trigger: Any invocation where the wrapped function call (func.call) returns Err: wrong arguments to the wrapped function, non-iterable inputs, or any failure of the inner dbt function being guarded. The outer try_or_compiler_error converts it into your message.
Common situations: Using try_or_compiler_error around functions like zip/set during model compilation and hitting failures on bad inputs; relying on the wrapper during migration debugging and needing the real underlying cause, which is swallowed by this message.
Understand the failure class
Background: "must be a positive integer", "cannot be empty", "invalid argument": how invalid-argument errors work across open-source libraries — this error's family across 33 libraries.
Related errors
- Argument must be a string
- argument 'name' to has_var() has incompatible type; value…
- argument 'name' to var() has incompatible type; value is…
- Column 'data_type' must be a string
- Column 'name' must be a string
AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07).
Data as JSON: /api/errors/03e86513eb561f33.
Report an issue: GitHub.
Appendix: source
Thrown at crates/dbt-jinja-utils/src/functions/base.rs:813
/// Example:
/// ```jinja
/// {% set result = try_or_compiler_error("Error", my_function, arg1, arg2, kwarg1="value1", kwarg2="value2") %}
/// ```
pub fn try_or_compiler_error_fn()
-> impl Fn(&State<'_, '_>, &[Value], Kwargs) -> Result<Value, Error> {
move |state: &State<'_, '_>, args: &[Value], kwargs: Kwargs| -> Result<Value, Error> {
let mut args = ArgParser::new(args, Some(kwargs));
let message_if_exception = args.get::<String>("message_if_exception")?;
let func = args.get::<Value>("func")?;
let mut remaining_args = args.get_args_as_vec_of_values();
let drained_kwargs = args.drain_kwargs();
let remaining_kwargs = Kwargs::from_iter(drained_kwargs);
remaining_args.push(remaining_kwargs.into());
match func.call(state, &remaining_args, &[]) {
Ok(result) => Ok(result),
// TODO: we need to raise CompilationError(message_if_exception, self.model)
Err(_) => Err(Error::new(
ErrorKind::InvalidOperation,
message_if_exception,
)),
}
}
}
/// Return an iterator of tuples where each tuple contains the i-th element from each of the input iterables.
/// dbt's zip also supports a custom kwarg `fillvalue` (default=None) to match the longest iterable.
///
/// Args:
/// *iterables: Two or more iterables
/// fillvalue: (optional) Value to fill in shorter iterables, default=None
///
/// Example:
/// ```jinja
/// {% set list1 = [1, 2] %}
/// {% set list2 = ['a', 'b', 'c'] %}View on GitHub (pinned to 0267ce9170)