## TL;DR

Something forces a full refresh on every run: a `--full-refresh` flag in the job command, a missing `unique_key`, or the target table being dropped between runs. Check the model config, the job command, and the table's existence, then run twice and confirm the second run is incremental.

## Error

```text
dbt incremental model broken: re-running full refresh every time
```

## Steps

1. Check the model config: it needs `materialized='incremental'` and, for merge-style increments, a `unique_key`. Expected: both are set correctly.
2. Check the job or command that runs it for a `--full-refresh` flag (common in dbt Cloud job settings or CI scripts). Expected: the flag is absent for normal runs.
3. Verify the target table still exists in the warehouse between runs; some pipelines drop it. Expected: the table persists across runs.
4. Inspect the `is_incremental()` branches in the model SQL: the incremental filter must actually restrict rows when true. Expected: the incremental branch is reachable and correct.
5. Run the model twice in a row and compare run times and the warehouse query history. Expected: the second run processes only new rows.

## When to use

- An incremental model takes full-refresh time on every run.
- Run logs show "full refresh" behavior (drop and recreate) unexpectedly.

## When not to use

- The model is configured as `materialized='table'` (full refresh is correct).
- You explicitly want full refreshes (then the behavior is intended).

## Tool compatibility

- dbt Core 1.0 and later, all adapters. Incremental strategies (merge, delete+insert, append) vary by adapter.

## Variant phrasings

### is_incremental() never true

The usual symptom: check unique_key, the existing table, and the --full-refresh flag.

### Incremental model slow as a full table build

Same problem; the incremental path is not being taken.

## Why it happens

dbt chooses full refresh when told to (`--full-refresh`), when it cannot merge (no `unique_key` on adapters that need one), or when the target relation is missing. Any of these silently turns every run into a full rebuild.

## Edge cases

- `on_schema_change` settings can trigger full refreshes when columns change; that is by design.
- dbt Cloud "run on schedule" jobs sometimes inherit a full-refresh checkbox someone ticked months ago.
- A failing incremental branch that falls back to full SQL still looks like a full refresh in timing; read the compiled SQL.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_ylcdTTBdfLkpSeKPOtbF1g
