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

  1. Inspect the wrapped function's inputs — the wrapper hides the underlying error, so validate arguments before the call
  2. Pass a descriptive message_if_exception so the thrown CompilationError pinpoints the model and inputs
  3. Temporarily replace try_or_compiler_error with a direct call to see the real underlying error
  4. 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

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


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)