dbt-labs/dbt-core · error

clean_sql

Error message

clean_sql

What it means

The `clean_sql` adapter method is available on concrete (typed/execution) adapters but not on the Parse adapter; calling it while the inner adapter is the `Parse` variant panics with `unimplemented!("clean_sql")`. During parsing (no database connection), SQL-cleaning operations that require the adapter's dialect runtime are unavailable. It reflects invoking an execution-time method during the parse phase of the dbt lifecycle.

Source

Thrown at crates/dbt-adapter/src/adapter/mod.rs:3593

            }
            Parse(_) => Ok(none_value()),
        }
    }

    /// Clean SQL by removing extra whitespace and normalizing format.
    ///
    /// Only available with Databricks adapter.
    #[tracing::instrument(skip_all, level = "trace")]
    pub fn clean_sql(&self, _state: &State, args: &[Value]) -> Result<Value, minijinja::Error> {
        match &self.inner {
            Typed { adapter, .. } => {
                let iter = ArgsIter::new("clean_sql", &["sql"], args);
                let sql = iter.next_arg::<&str>()?;
                iter.finish()?;

                Ok(Value::from(adapter.clean_sql(sql)?))
            }
            Parse(_) => unimplemented!("clean_sql"),
        }
    }

    /// Used internally to attempt executing a Snowflake `use warehouse [name]` statement.
    #[tracing::instrument(skip(self), level = "trace")]
    pub fn use_warehouse(
        &self,
        warehouse: Option<String>,
        node_id: &str,
    ) -> FsResult<Option<NodeOverride>> {
        // TODO(jason): Record/replay non-jinja internal calls non-invasively
        // https://github.com/dbt-labs/fs/issues/7736
        if let Some(tm) = self.time_machine()
            && tm.is_replaying()
        {
            return Ok(None);
        }

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Move the clean_sql call into code that only executes during execution (not parse), e.g. guard with execute check (`{% if execute %}`).
  2. Skip SQL cleaning during parse — return the SQL unmodified when the adapter is a Parse adapter.
  3. Check whether the macro should be in a parse-time block at all; parse-time code must avoid adapter runtime methods.

Example fix

// before
{% set cleaned = adapter.clean_sql(sql) %}
// after
{% if execute %}
  {% set cleaned = adapter.clean_sql(sql) %}
{% else %}
  {% set cleaned = sql %}
{% endif %}
Defensive patterns

Strategy: validation

Validate before calling

// Jinja: only call clean_sql during execution
{% set can_clean = execute %}

Type guard

{% if execute %} ... {% endif %} — 'execute' is false during parse, true at runtime.

Try / catch

// Parse-time code must not call clean_sql
{% if execute %}
  {% set cleaned = adapter.clean_sql(sql) %}
{% else %}
  {% set cleaned = sql %}
{% endif %}

Prevention

When it happens

Trigger: Calling `adapter.clean_sql(sql)` from a macro or internal code while the Adapter wraps the Parse engine — i.e. during the parse/compile phase before a real adapter is attached.

Common situations: Macros that run during parsing but call clean_sql; dbt parse (`dbt parse`) or partial-parse runs hitting macros that assume a live connection; custom materializations invoking clean_sql unconditionally.

Related errors


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