dbt-labs/dbt-core · error

Schema not found for canonical FQN: {}

Error message

Schema not found for canonical FQN: {}

What it means

DelayedTableProvider caches Arrow schemas keyed by canonical fully-qualified table name in a schema_store. When `schema()` is called, the entry for this table's canonical FQN must already have been populated by an earlier registration step; if it is absent, the code panics rather than returning a wrong or empty schema. This is an internal invariant: a schema should never be requested before it is registered.

Source

Thrown at crates/dbt-df-providers/src/delayed_table.rs:78

            schema_store: schema_cache,
            data_store,
            data_provider: OnceLock::new(),
        }
    }
}

#[async_trait::async_trait]
impl TableProvider for DelayedDataTableProvider {
    fn as_any(&self) -> &dyn Any {
        self
    }

    fn schema(&self) -> Arc<arrow_schema::Schema> {
        let schema_entry = self
            .schema_store
            .get_schema(&self.canonical_fqn)
            .unwrap_or_else(|| {
                panic!("Schema not found for canonical FQN: {}", self.canonical_fqn);
            });
        schema_entry.inner().clone()
    }

    /// Ensures the physical data provider exists before delegating to it.
    async fn scan(
        &self,
        state: &dyn Session,
        projection: Option<&Vec<usize>>,
        filters: &[Expr],
        limit: Option<usize>,
    ) -> Result<Arc<dyn datafusion::physical_plan::ExecutionPlan>, DataFusionError> {
        // Set the data provider here...
        let schema = self.schema();
        let provider = if let Some(provider) = self.data_provider.get() {
            provider.clone()
        } else {
            let provider = make_listing_table_provider(

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Ensure the pipeline registers the schema via schema_store.get/insert with the exact same canonical FQN before any schema()/scan() call.
  2. Log and compare the canonical_fqn used at registration time against the one used at lookup to find qualification mismatches.
  3. Verify the upstream model/table actually completed building before querying the delayed provider.
  4. Replace the panic with a fallible schema() returning Result so callers can surface 'schema not yet available' cleanly.
Defensive patterns

Strategy: validation

Validate before calling

// before requesting the schema, verify registration happened
if schema_store.get_schema(&canonical_fqn).is_none() {
    return Err(anyhow!("schema for {} not yet registered", canonical_fqn));
}

Try / catch

// use a fallible accessor instead of the panicking schema()
match provider.try_schema() {
    Ok(schema) => schema,
    Err(e) => bail!("delayed table {} has no registered schema: {}", fqn, e),
}

Prevention

When it happens

Trigger: Calling `schema()` (directly or via `scan()`) on a DelayedTableProvider whose canonical_fqn was never inserted into schema_store — e.g. the registration/planning step that populates the store was skipped, the FQN normalization differs between writer and reader, or a race dropped the entry before read.

Common situations: Querying a dbt model's table relation before the model finished building; a plan referencing a relation whose schema registration failed earlier in the pipeline; case- or catalog-qualification mismatches producing a canonical FQN that doesn't equal the key used at registration.

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/4e18a5c3ff01ba0a. Report an issue: GitHub.