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
- Pass exactly one string argument: render(some_string)
- Call render once per string if you have several to render
- 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
- Call render once per string, never with multiple args
- Concatenate strings (~) before rendering rather than passing several
- Remember kwargs are not counted as arguments
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
- diff_of_two_dicts requires exactly 2 arguments
- set() requires 1 argument
- set_strict requires exactly 1 argument
- zip requires at least 1 argument
- has_var requires 1 argument
AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07).
Data as JSON: /api/errors/6c122ef55635d577.
Report an issue: GitHub.