dbt-labs/dbt-core · warning

Both zorder and liquid_clustering are set but they are…

Error message

Both zorder and liquid_clustering are set but they are incompatible. zorder will be ignored.

What it means

Delta tables support either ZORDER optimization or liquid clustering, but not both. In `get_optimize_sql`, if the model config sets both `zorder` and (`liquid_clustered_by` or `auto_liquid_cluster`), dbt warns that zorder will be ignored and emits `optimize` without a zorder clause.

Solutions

  1. Remove the `zorder` config from the model since liquid clustering takes precedence
  2. Or remove `liquid_clustered_by`/`auto_liquid_cluster` if you intend to keep ZORDER
  3. Centralize clustering config in one place (e.g. a shared macro) so both keys can't be set simultaneously

Example fix

# before
{{ config(materialized='incremental', zorder=['user_id'], liquid_clustered_by=['user_id']) }}
# after
{{ config(materialized='incremental', liquid_clustered_by=['user_id']) }}
Defensive patterns

Strategy: validation

Validate before calling

def check_clustering_conflict(model):
    cfg = model.get('config', {})
    if cfg.get('zorder') and (cfg.get('liquid_clustered_by') or cfg.get('auto_liquid_cluster')):
        raise ValueError(f"{model['name']}: set both zorder and liquid clustering; zorder will be ignored")

Prevention

When it happens

Trigger: A Delta model sets `config.zorder` AND (`config.liquid_clustered_by` or `config.auto_liquid_cluster`); the `optimize` operation runs (post-hook/on incremental optimize).

Common situations: Enabling auto liquid clustering on an existing table that still carries a legacy `zorder` config; team-wide config defaults adding liquid clustering while individual models set zorder; switching clustering strategy without cleaning up the old key.

Related errors


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

Appendix: source

Thrown at crates/dbt-loader/src/dbt_macro_assets/dbt-databricks/macros/relations/optimize.sql:22

{%- macro databricks__optimize(relation) -%}
  {%- if config.get('skip_optimize', false) | as_bool -%}
  {%- elif var('DATABRICKS_SKIP_OPTIMIZE', 'false')|lower != 'true' and
        var('databricks_skip_optimize', 'false')|lower != 'true' and
        adapter.resolve_file_format(config) == 'delta' -%}
    {%- if (config.get('zorder', False) or config.get('liquid_clustered_by', False)) or config.get('auto_liquid_cluster', False) -%}
      {%- call statement('run_optimize_stmt') -%}
        {{ get_optimize_sql(relation) }}
      {%- endcall -%}
    {%- endif -%}
  {%- endif -%}
{%- endmacro -%}

{%- macro get_optimize_sql(relation) %}
  optimize {{ relation.render() }}
  {%- if config.get('zorder', False) and adapter.resolve_file_format(config) == 'delta' %}
    {%- if config.get('liquid_clustered_by', False) or config.get('auto_liquid_cluster', False) %}
      {{ exceptions.warn("Both zorder and liquid_clustering are set but they are incompatible. zorder will be ignored.") }}
    {%- else %}
      {%- set zorder = config.get('zorder', none) %}
      {# TODO: predicates here? WHERE ...  #}
      {%- if zorder is sequence and zorder is not string %}
        zorder by (
        {%- for col in zorder %}
        {{ col }}{% if not loop.last %}, {% endif %}
        {%- endfor %}
        )
      {%- else %}
        zorder by ({{zorder}})
      {%- endif %}
    {%- endif %}
  {%- endif %}
{%- endmacro -%}

View on GitHub (pinned to 0267ce9170)