dbt-labs/dbt-core · error · AdapterError
'{strategy}' strategy does not support unique_key.
Error message
'{strategy}' strategy does not support unique_key. What it means
insert_overwrite replaces entire partitions, so per-row key matching via unique_key is incompatible with it on ClickHouse. validate_incremental_strategy raises this Configuration error when insert_overwrite is combined with a unique_key config.
Source
Thrown at crates/dbt-adapter/src/metadata/clickhouse/mod.rs:927
));
}
if matches!(strategy, "delete_insert" | "microbatch") && !has_unique_key {
return err(format!(
"'{strategy}' strategy requires a non-empty 'unique_key'."
));
}
if !matches!(strategy, "delete_insert" | "microbatch") && has_predicates {
return err(format!(
"Cannot apply incremental predicates with '{strategy}' strategy."
));
}
if strategy == "insert_overwrite" && !has_partition_by {
return err(format!(
"'{strategy}' strategy requires non-empty 'partition_by'. Current partition_by is None."
));
}
if strategy == "insert_overwrite" && has_unique_key {
return err(format!(
"'{strategy}' strategy does not support unique_key."
));
}
Ok(())
}
/// Mirrors column.py `ClickHouseColumnChanges` as a Jinja value: none when
/// there are no changes (its `__bool__`, so `{% if column_changes %}` skips),
/// else a map; errors when the strategy cannot reconcile the changes.
pub(crate) fn column_changes_value(
on_schema_change: &str,
source: Vec<Column>,
target: Vec<Column>,
materialization: &str,
) -> AdapterResult<Value> {
let in_target = |name: &str| target.iter().any(|c| c.name() == name);
let source_of = |name: &str| source.iter().find(|c| c.name() == name);
View on GitHub (pinned to 0267ce9170)
Solutions
- Remove unique_key from the model config when using insert_overwrite
- Use partition_by to scope which data gets replaced instead
- If per-key upsert semantics are needed, switch back to delete_insert and keep unique_key
Example fix
// before
{{ config(materialized='incremental', incremental_strategy='insert_overwrite', partition_by='toDate(event_ts)', unique_key='id') }}
// after
{{ config(materialized='incremental', incremental_strategy='insert_overwrite', partition_by='toDate(event_ts)') }} Defensive patterns
Strategy: validation
Validate before calling
if cfg.incremental_strategy == "insert_overwrite" && cfg.unique_key.is_some() {
panic!("unique_key is incompatible with insert_overwrite");
} Prevention
- Remove unique_key when switching to insert_overwrite
- Avoid shared config macros that inject unique_key unconditionally
- Model partition-level replacement intent with partition_by, not keys
When it happens
Trigger: Setting incremental_strategy='insert_overwrite' together with unique_key on a ClickHouse incremental model — usually left over from a previous delete_insert/delete+insert configuration.
Common situations: Flipping incremental_strategy from delete_insert to insert_overwrite without removing unique_key; a shared macro/base config injecting unique_key for all incremental models.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- The incremental strategy '{strategy}' is not valid for Click
- '{strategy}' strategy requires lightweight deletes, but the
- '{strategy}' strategy requires a non-empty 'unique_key'.
- Cannot apply incremental predicates with '{strategy}' strate
- '{strategy}' strategy requires non-empty 'partition_by'. Cur
AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07).
Data as JSON: /api/errors/25154827801cf6bb.
Report an issue: GitHub.