dbt-labs/dbt-core · error

ModelPropertiesSemanticModelConfig is never finalized direct

Error message

ModelPropertiesSemanticModelConfig is never finalized directly

What it means

ModelPropertiesSemanticModelConfig is an intermediate 'draft' config that implements a finalize trait, but by design it is never finalized as-is; it must first be converted/merged into another config form. Calling finalize() on it directly indicates a pipeline bug where the wrong config type reached the resolution step. The panic is a guard that surfaces such misuse immediately instead of producing a half-resolved semantic model.

Source

Thrown at crates/dbt-schemas/src/schemas/properties/model_properties.rs:102

}

impl ResolvableConfig<SemanticModelConfig> for ModelPropertiesSemanticModelConfig {
    type Resolved = ResolvedSemanticModelConfig;
    type PackageDefaults = ();
    type ResolveDefaults = ();

    fn get_enabled_with_default(&self) -> bool {
        self.enabled
    }

    fn disable(&mut self) {
        self.enabled = false;
    }

    fn apply_package_defaults(&mut self, _: ()) {}

    fn finalize(self) -> ResolvedSemanticModelConfig {
        unreachable!("ModelPropertiesSemanticModelConfig is never finalized directly")
    }

    fn default_to(&mut self, parent: &SemanticModelConfig) {
        DefaultTo::inherit_from(&mut self.group, &parent.group);
    }
}

impl ModelProperties {
    pub fn empty(name: String) -> Self {
        Self {
            name,
            columns: None,
            config: None,
            constraints: None,
            data_tests: None,
            deprecation_date: None,
            description: None,
            identifier: None,

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Do not call finalize() on ModelPropertiesSemanticModelConfig directly; finalize the containing/wrapper config instead.
  2. Locate the resolution call site and ensure the semantic model config is converted to the final ResolvedSemanticModelConfig via the intended wrapper path.
  3. If adding a new config type, implement finalize() with a real conversion rather than routing through this placeholder.
  4. Check for recent refactors in properties model resolution that may have swapped config types.

Example fix

// before
let resolved = semantic_cfg.finalize()?; // panics: draft type never finalized directly

// after
let resolved = properties.semantic_model.unwrap().finalize()?; // finalize the wrapper that owns the conversion
Defensive patterns

Strategy: type-guard

Validate before calling

if cfg.is_draft_intermediate() { /* route through wrapper, not finalize */ }

Type guard

fn is_finalizable_directly<T: Finalize>(_cfg: &T) -> bool { false } // draft types are never finalized directly; finalize their wrapper instead

Prevention

When it happens

Trigger: Invoking DefaultTo/finalize resolution machinery with a ModelPropertiesSemanticModelConfig value instead of the wrapper type that is actually meant to be finalized.

Common situations: Refactoring the properties-resolution pipeline so a semantic model config bypasses its wrapper; writing new code that calls finalize() generically on any config implementing the trait; custom tooling that reuses dbt-schemas config types and calls finalize directly.

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/785240f364e204df. Report an issue: GitHub.