dbt-labs/dbt-core · error

metadata query plan always includes metadata SQL

Error message

metadata query plan always includes metadata SQL

What it means

In `freshness_inner_with_options` (crates/dbt-adapter/src/metadata/snowflake/mod.rs:640), the code builds a metadata query plan via `snowflake_metadata_query_plan` and asserts `.statements.last()` exists. The panic means the plan builder returned an empty statement list, violating its contract that the metadata SQL is always appended last.

Source

Thrown at crates/dbt-adapter/src/metadata/snowflake/mod.rs:640

            Box::new(AdapterConnectionFactory::new(
                self.adapter.engine().clone(),
                self.adapter.engine().threads(),
            )),
        ));

        let adapter = self.adapter.clone();
        let token_clone = token.clone();
        let map_f = move |conn: &'_ mut dyn Connection,
                          database_and_where_clauses: &(String, Vec<String>)|
              -> AdapterResult<Arc<RecordBatch>> {
            let (database, where_clauses) = &database_and_where_clauses;
            let sql = snowflake_freshness_sql(database, where_clauses)?;

            let plan = snowflake_metadata_query_plan(&sql, metadata_warehouse.as_deref());
            let metadata_sql = plan
                .statements
                .last()
                .expect("metadata query plan always includes metadata SQL");

            let ctx = QueryCtx::default().with_desc("Extracting freshness from information schema");
            let (_adapter_response, agate_table) =
                adapter.query(&ctx, conn, metadata_sql, None, token_clone.clone())?;
            let batch = agate_table.original_record_batch();
            Ok(batch)
        };

        let reduce_f = move |acc: &mut Acc,
                             database_and_where_clauses: (String, Vec<String>),
                             batch_res: AdapterResult<Arc<RecordBatch>>|
              -> Result<(), Cancellable<AdapterError>> {
            let Ok(batch) = batch_res else {
                // Keep successful database batches; missing relations fall back downstream.
                return Ok(());
            };
            let schemas = batch.column_values::<StringArray>("TABLE_SCHEMA")?;
            let tables = batch.column_values::<StringArray>("TABLE_NAME")?;

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Fix snowflake_metadata_query_plan to always append the metadata SQL statement (return an error on empty instead)
  2. Replace `.expect` with error propagation reporting the empty plan
  3. Add a unit test asserting the plan always ends with the metadata statement
  4. Check that snowflake_freshness_sql is not producing empty SQL for the given inputs

Example fix

// before
.expect("metadata query plan always includes metadata SQL")
// after
.ok_or_else(|| AdapterError::new(AdapterErrorKind::UnexpectedResult, "empty metadata query plan".into()))?
Defensive patterns

Strategy: validation

Validate before calling

// preflight: plan must be non-empty
let plan = snowflake_metadata_query_plan(&sql, warehouse);
if plan.statements.is_empty() { return Err(/* error */); }

Type guard

fn plan_has_metadata_sql(plan: &QueryPlan) -> bool {
    !plan.statements.is_empty()
}

Try / catch

let metadata_sql = plan.statements.last().ok_or_else(|| AdapterError::new(...))?;

Prevention

When it happens

Trigger: `snowflake_metadata_query_plan` returning a plan with zero statements — typically after a refactor of the plan builder or an empty/invalid freshness SQL input it silently accepts.

Common situations: Post-refactor regressions in snowflake_metadata_query_plan; edge-case where the freshness SQL builder produced empty SQL that the planner dropped; feature flags stripping warehouse-prefix statements.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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