dbt-core 2.0.5 - BigQuery Incrementals Stay on MERGE


dbt-core v2.0.5 was published on 18 September 2026 at 15:47 UTC. The patch contains one BigQuery driver fix. The Legacy driver and the Foundry driver now continue when ADBC.GetObjects returns 404 or 403, so incremental models stay on MERGE instead of being rebuilt with REPLACE TABLE.

The full release notes and downloads are on the GitHub release page.

Issue 16331 is the report this patch addresses. Two runs share one BigQuery dataset. One run drops a __dbt_tmp table while the other lists that dataset into the relation cache. ADBC.GetObjects lists the tables, then reads metadata for each one. The dropped table returns 404. On the v2 builds named in the report, the driver failed the whole listing.

The v2 engine then stored that not found result as an empty schema and marked the listing complete. Later lookups missed tables that still existed. is_incremental() returned false for every incremental model in the run. The materialization took the missing relation branch and submitted create or replace table over the full source query. The run logged no warning, exited 0, and submitted no MERGE.

The report counts the hits. On dbt 2.0.2, 12 of 20 runs replaced all four incremental models. On dbt-fusion 2.0.0-preview.218, 6 of 25 runs did that, and 12 of 25 did it on a fresh dataset. The same churn against dbt-core 1.12.4 with dbt-bigquery 1.12.0 replaced zero models in 15 runs.

A good run and a bad run print the same debug lines for the relation download. The bad run then submits create or replace table once per incremental model. Default logs stay quiet. The replace shows up in INFORMATION_SCHEMA.JOBS_BY_USER. The production note describes a burst of INFORMATION_SCHEMA.ROUTINES queries, then one replace per incremental model. A healthy run issues one routines query. That deployment saw 12 recreations of a six model chain in two weeks, about 870 GiB and $5.3 each. Hit rate follows tables.list order, because the driver reads metadata in that order. One failed listing covers every incremental model in the invocation.

v2.0.5 makes the driver continue on that 404, so the listing can finish. Tables that remain can still fill the cache, and is_incremental() can see the models that are actually there.

REPLACE TABLE in the v2.0.5 notes is this create or replace table path. MERGE is what the incremental materialization emits when incremental_strategy is merge and is_incremental() is true. The models in the report set materialized to incremental, unique_key to id, and incremental_strategy to merge.

MERGE updates rows on the matching key and inserts the rest. create or replace table builds a new table from the select for that run and puts it in place of the old one. A filter wrapped in is_incremental() is skipped when the function is false, so the select reads the full upstream relation. When that select is not the full history, the replace stores a shorter table and the previous rows are gone. The task still exits 0. A scheduler that only watches process status records a success.

The notes include 403 in the same fix. A metadata read that returns 403 during ADBC.GetObjects could fail the schema listing the same way a missing temp table did. The object that returned 404 or 403 is left out of the listing. The rest of the dataset stays in the cache. A dropped __dbt_tmp table should be left out. A real model table that vanishes during the listing can still look absent, and that one model can still take the replace path. Sibling models stay on merge.

The notes stop at that driver change. They do not mention a new warning or a second look at an empty listing through INFORMATION_SCHEMA.TABLES.

The Legacy driver and the Foundry driver both continue on ADBC.GetObjects 404 and 403. The listing step runs once per dataset per invocation, before model SQL. One bad listing used to replace every incremental model in that dataset. On v2.0.5 those models stay on the configured strategy when the only error is a 404 or 403 on some other object. serramatutu is the contributor listed on the notes, against issue 16331.

The notes list no config key and no CLI flag. Move to v2.0.5 when BigQuery incremental models on the v2 engine share a dataset with another job that creates and drops __dbt_tmp tables.

Rows a bad run already replaced stay replaced. Put them back from a snapshot, from a BigQuery time travel read, or from a rebuild whose upstream still holds the history. After the install, read INFORMATION_SCHEMA.JOBS_BY_USER on a dataset with concurrent runs. Existing models should show MERGE, or the strategy set on the model. create or replace table should appear only when a full rebuild was requested.