dbt-labs/dbt-core · error · InvalidOperation

render requires exactly one argument (the string to render)

Error message

render requires exactly one argument (the string to render)

What it means

render() takes a string, renders it as a Jinja template against the current state, and returns the result. The function requires exactly one positional argument; any other count (zero, or two or more) throws this InvalidOperation error before any rendering happens.

Source

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

/// Renders a string as a Jinja template using the current context.
///
/// Args:
///     sql: The string to render as a template.
///
/// Example:
/// ```jinja
/// {% set rendered = render("Hello {{ this.name }}") %}
/// ```
///
/// Returns:
///     The rendered string with all template expressions evaluated in the current context.
///
/// Errors:
///     Raises an error if the argument is not a string or if rendering fails.
pub fn render_fn() -> impl Fn(&State, &[Value], Kwargs) -> Result<Value, Error> {
    move |state: &State, args: &[Value], _kwargs: Kwargs| -> Result<Value, Error> {
        if args.len() != 1 {
            return Err(Error::new(
                ErrorKind::InvalidOperation,
                "render requires exactly one argument (the string to render)",
            ));
        }
        // dbt-core (Jinja2/Python) effectively accepts any value here and stringifies it.
        // In practice, many dbt projects call `render(...)` on values that can legitimately be
        // `none` (e.g. optional metadata-driven SQL snippets from `run_query`), expecting
        // `"None"` and handling that downstream.
        //
        // Fusion uses minijinja which is stricter by default; align behavior by accepting
        // `none` and treating it like Python's `str(None)` => `"None"`.
        let sql = if args[0].is_none() {
            "None"
        } else {
            args[0].as_str().ok_or_else(|| {
                Error::new(ErrorKind::InvalidOperation, "Argument must be a string")
            })?
        };

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Pass exactly one string argument: render(some_string)
  2. Call render once per string if you have several to render
  3. If the value may be undefined, coalesce first: render(var if var is defined else '')

Example fix

// before
{% set out = render(sql_a, sql_b) %}
// after
{% set out = render(sql_a ~ sql_b) %}
Defensive patterns

Strategy: validation

Validate before calling

{% if template_str is not string %}
  {{ exceptions.raise_compiler_error('render() needs a string in ' ~ this) }}
{% endif %}
{% set out = render(template_str) %}

Type guard

{% macro is_renderable(x) %}{{ return(x is string or x is none) }}{% endmacro %}

Prevention

When it happens

Trigger: Calling render() with no argument, or render(a, b) with multiple arguments, from a model or macro template. The count check happens before the value is even inspected.

Common situations: Attempting to render multiple strings in one call instead of calling render once per string; refactors that dropped the argument; passing kwargs expecting them to count as arguments (they don't).

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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