dbt-labs/dbt-core · warning

contract.enforced=true on materialized_view

Error message

contract.enforced=true on materialized_view '{{ model.name }}': not supported by dbt (https://docs.getdbt.com/docs/mesh/govern/model-contracts). dbt-databricks provides best-effort support that may change without notice.

What it means

dbt core does not support model contracts on materialized views. When a dbt-databricks materialized view has `contract.enforced: true`, the adapter warns that its contract support is best-effort and may change, then still reads model constraints/columns to build the view. It is a heads-up that contract behavior here is non-standard and unprotected by core guarantees.

Solutions

  1. Remove `contract.enforced` (or set it false) for materialized_view models
  2. Scope the contract config to supported materializations only (tables/views) via `config` selectors or per-model YAML
  3. If you rely on it, pin dbt-databricks versions and re-validate MV contract behavior on upgrades

Example fix

# before
models:
  - name: my_mv
    config:
      materialized: materialized_view
      contract:
        enforced: true
# after
models:
  - name: my_mv
    config:
      materialized: materialized_view
# keep contracts on table models only
Defensive patterns

Strategy: validation

Validate before calling

def check_contract_on_mv(model):
    if model.get('config', {}).get('materialized') == 'materialized_view':
        assert not model.get('config', {}).get('contract', {}).get('enforced'), \
            f"{model['name']}: contract.enforced not supported on materialized_view"

Prevention

When it happens

Trigger: A materialized view model sets `config.contract.enforced=true` (via YAML config or `{{ config(...) }}`) and is built with dbt-databricks.

Common situations: Applying a shared `contract` config block across all models including MVs; adopting contracts for governance and applying them uniformly; copy-pasted model configs from table models.

Related errors


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

Appendix: source

Thrown at crates/dbt-loader/src/dbt_macro_assets/dbt-databricks/macros/relations/materialized_view/create.sql:32

  {# DIVERGENCE END #}

  {#
    TODO: When DESCRIBE QUERY EXTENDED is supported, this implementation should be simplified
    to use that instead. For now, we work around this limitation by writing results to a
    temporary view and using DESCRIBE TABLE EXTENDED on the temporary view.
  #}
  {%- set temp_relation = make_temp_relation(relation) -%}
  {% call statement('create_temp_view') -%}
    {%- set sql_with_limit = sql.rstrip('; \n\t') ~ ' LIMIT 10' -%}
    {{ create_temporary_view(temp_relation, sql_with_limit) }}
  {%- endcall %}

  {%- set columns = adapter.get_columns_in_relation(temp_relation) -%}
  {%- set model_columns = model.get('columns', {}) -%}
  {%- set contract_config = config.get('contract') -%}
  {%- set contract_enforced = contract_config and contract_config.enforced -%}
  {%- if contract_enforced -%}
    {%- do exceptions.warn(
         "contract.enforced=true on materialized_view '" ~ model.name ~ "': not supported by dbt (https://docs.getdbt.com/docs/mesh/govern/model-contracts). dbt-databricks provides best-effort support that may change without notice."
       ) -%}
    {%- set model_constraints = model.get('constraints', []) -%}
  {%- else -%}
    {%- set model_constraints = [] -%}
  {%- endif -%}
  {%- set columns_and_constraints = adapter.parse_columns_and_constraints(columns, model_columns, model_constraints, contract_enforced, model.name) -%}
  {%- set target_relation = relation.enrich(columns_and_constraints[1]) -%}

  create or replace materialized view {{ target_relation.render() }}
    {{ get_column_and_constraints_sql(target_relation, columns_and_constraints[0]) }}
    {{ get_create_row_filter_clause(target_relation) }}
    {{ get_create_sql_partition_by(partition_by) }}
    {{ liquid_clustered_cols() }}
    {{ get_create_sql_comment(comment) }}
    {{ get_create_sql_tblproperties(tblproperties) }}
    {{ get_create_sql_refresh_schedule(refresh.cron, refresh.time_zone_value) }}
  as

View on GitHub (pinned to 0267ce9170)