windmill-labs/windmill · error

`{{{{ {name} }}}}` collides with the run argument `{name}` t

Error message

`{{{{ {name} }}}}` collides with the run argument `{name}` this runtime already defines ({}); rename the placeholder

What it means

When parsing a dbt descriptor YAML, windmill collects the `{{ ... }}` placeholder names used in the descriptor and refuses any placeholder whose name is one of the reserved run-argument names (RESERVED_ARG_NAMES, listed in the message). Such a placeholder would collide with a run argument the dbt runtime defines itself, so parse_dbt_descriptor rejects the descriptor with this error.

Source

Thrown at backend/parsers/windmill-parser-yaml/src/dbt.rs:274

    "command",
    "select",
    "exclude",
    "vars",
    "full_refresh",
    "dbt_command",
    "dbt_retry_job",
    "model",
    "limit",
];

pub fn parse_dbt_descriptor(inner_content: &str) -> anyhow::Result<DbtDescriptor> {
    let d = serde_yml::from_str::<DbtDescriptor>(inner_content)
        .map_err(|e| anyhow::anyhow!("Failed to parse dbt descriptor: {e}"))?;
    if let Some(name) = placeholders(&d)
        .into_iter()
        .find(|n| RESERVED_ARG_NAMES.contains(&n.as_str()))
    {
        return Err(anyhow::anyhow!(
            "`{{{{ {name} }}}}` collides with the run argument `{name}` this runtime already \
             defines ({}); rename the placeholder",
            RESERVED_ARG_NAMES.join(", ")
        ));
    }
    Ok(d)
}

/// The workspace warehouse a descriptor gets when it names none — spelled like
/// the default lake (`ducklake://main.orders`), so one workspace concept reads
/// the same across kinds.
pub const DBT_DEFAULT_WAREHOUSE: &str = "main";

/// The single argument holding the command and the overrides it takes. Its
/// variant IS the command, so a run cannot carry an override the command
/// ignores: `retry` rebuilds the failed run's nodes with the arguments that run
/// had, and `full_refresh` means nothing to a `show`.
pub const DBT_COMMAND_ARG: &str = "command";

View on GitHub (pinned to e474e8803c)

Solutions

  1. Rename the placeholder in the descriptor YAML to a non-reserved name (and update its references in the code).
  2. Check the list of reserved names printed in the error message and pick a name outside it.
  3. Use the dedicated descriptor fields/options instead of a placeholder when you intend to override a built-in run argument.

Example fix

// before
descriptor: "run --select {{{{ dbt }}}}_model"
// after
descriptor: "run --select {{{{ model_name }}}}"
Defensive patterns

Strategy: validation

Validate before calling

fn placeholder_ok(name: &str, reserved: &[&str]) -> bool { !reserved.contains(&name) }
// scan descriptor YAML for {{{{ name }}}} placeholders and reject reserved names before parse

Type guard

fn is_reserved(name: &str, reserved: &[&str]) -> bool { reserved.contains(&name) }

Try / catch

match parse_dbt_descriptor(yaml) {
    Ok(d) => d,
    Err(e) if e.to_string().contains("collides with the run argument") => {
        // rename the placeholder listed in the message and retry
        Err(e)
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: parse_dbt_descriptor is called by parse_dbt_sig, dbt_arg_schema and related descriptor parsing (also exercised by tests like a_placeholder_may_not_take_a_run_argument_name); it errors whenever a `{{{{ name }}}}` placeholder in the dbt descriptor YAML matches an entry of RESERVED_ARG_NAMES, e.g. naming a placeholder after a built-in run argument.

Common situations: A developer writes a dbt script whose descriptor uses `{{ dbt }}`, `{{ target }}` or similar, not realizing the runtime already injects that name as a run argument; often a copy-paste from docs using a reserved word as a variable name.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/004c4680d12c8c44. Report an issue: GitHub.