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
- Move the clean_sql call into code that only executes during execution (not parse), e.g. guard with execute check (`{% if execute %}`).
- Skip SQL cleaning during parse — return the SQL unmodified when the adapter is a Parse adapter.
- 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
- Wrap adapter runtime methods in `{% if execute %}` blocks
- Test macros with `dbt parse` to catch parse-phase misuse
- Never call connection-dependent adapter methods from parse-time contexts
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.