dbt-labs/dbt-core · error

when data_type is date, inner must be a TimeConfig

Error message

when data_type is date, inner must be a TimeConfig

What it means

This panic fires inside BigQuery partition config logic when a partition whose data_type is "date" is backed by a Range config instead of a Time config. The library treats that combination as logically impossible: data_type "date" is only meaningful for time-based partitioning with day granularity, so the match arm for Range is marked unreachable. Reaching it means an inconsistent BigqueryPartitionConfig was constructed without being validated.

Solutions

  1. Fix the config so that data_type "date" pairs with the Time inner variant and granularity "day".
  2. Ensure configs go through the serde deserializer/validation path instead of being constructed field-by-field in code.
  3. If building configs programmatically, use the constructor that derives __inner__ from data_type rather than setting both independently.
  4. Check for version skew: older serialized manifests may encode a shape the current code considers impossible; re-deserialize with the matching schema version.

Example fix

// before (invalid config)
BigqueryPartitionConfig { data_type: "date".into(), __inner__: BigqueryPartitionConfigInner::Range(range_cfg), .. }

// after
BigqueryPartitionConfig { data_type: "date".into(), __inner__: BigqueryPartitionConfigInner::Time(TimeConfig { granularity: "day".into(), ..Default::default() }), .. }
Defensive patterns

Strategy: validation

Validate before calling

fn is_valid_partition(cfg: &BigqueryPartitionConfig) -> bool {
    !(cfg.data_type == "date" && matches!(cfg.__inner__, BigqueryPartitionConfigInner::Range(_)))
}

Type guard

fn as_time_inner(inner: &BigqueryPartitionConfigInner) -> Option<&TimeConfig> {
    match inner { BigqueryPartitionConfigInner::Time(t) => Some(t), _ => None }
}

Prevention

When it happens

Trigger: Calling data_type_should_be_truncated (directly or via render/render_wrapped) on a BigqueryPartitionConfig whose __inner__ is BigqueryPartitionConfigInner::Range while data_type == "date".

Common situations: Hand-constructing a BigqueryPartitionConfig from deserialized dbt config where data_type and the inner variant were set independently (e.g. a user's yml sets `partition_by: {type: date}` but the config was normalized into a range partition), or bypassing serde validation when programmatically building configs.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at crates/dbt-schemas/src/schemas/manifest/bigquery_partition.rs:211

            Ok(MinijinjaValue::from(columns))
        } else {
            Err(MinijinjaError::new(
                MinijinjaErrorKind::InvalidArgument,
                "columns must be a list of Column",
            ))
        }
    }

    /// Return true if the data type should be truncated instead of cast to the data type
    pub fn data_type_should_be_truncated(&self) -> bool {
        !(self.data_type == "int64"
            || (self.data_type == "date"
                && match &self.__inner__ {
                    BigqueryPartitionConfigInner::Time(TimeConfig { granularity, .. }) => {
                        granularity == "day"
                    }
                    BigqueryPartitionConfigInner::Range(_) => {
                        unreachable!("when data_type is date, inner must be a TimeConfig")
                    }
                }))
    }

    /// Return the time partitioning field name based on the data type.
    /// The default is _PARTITIONTIME, but for date it is _PARTITIONDATE
    pub fn time_partitioning_field(&self) -> Result<MinijinjaValue, MinijinjaError> {
        let field = if self.data_type == "date" {
            Self::PARTITION_DATE
        } else {
            Self::PARTITION_TIME
        };
        Ok(MinijinjaValue::from(field))
    }

    /// Return the insertable time partitioning field name based on the data type.
    /// Practically, only _PARTITIONTIME works so far.
    pub fn insertable_time_partitioning_field(&self) -> Result<MinijinjaValue, MinijinjaError> {

View on GitHub (pinned to 0267ce9170)