Skip to content
>GLB_
Go back

Running the Tuva Project on DuckDB — what breaks and how to fix it

Running the Tuva Project locally on DuckDB is attractive — no warehouse costs, no cloud credentials during development, sub-second iteration. But the combination of Tuva 0.17.2, dbt 1.11, and DuckDB 1.10 has real rough edges that aren’t documented anywhere.

Here is what actually broke on a 167k claims dataset, and how each failure was resolved.

The setup

Stack:

Running dbt run on the full Tuva package processes 879 models on this dataset.

Error 1: undefined macro 'limit_zero'

A batch of models fail during compilation with:

Compilation Error in model quality_measures__int_adh_statins_numerator
  'limit_zero' is undefined. Did you mean one of these: dbt_utils.limit_zero

Tuva uses limit_zero internally to generate empty table structures during schema materialization. The macro exists inside dbt-utils but dbt’s macro resolution doesn’t automatically cross package boundaries without an explicit namespace prefix.

When dbt compiles a Tuva model that calls {{ limit_zero() }}, it looks in:

  1. Your project’s macros/ directory
  2. The Tuva package’s own macros
  3. The global dbt namespace

It does not walk into dbt-utils unless the Tuva macro was written as {{ dbt_utils.limit_zero() }}. In some versions it’s written as just {{ limit_zero() }}, which breaks.

Fix: create macros/limit_zero.sql in your local project:

{% macro default__limit_zero() %}
    limit 0
{% endmacro %}

{% macro duckdb__limit_zero() %}
    limit 0
{% endmacro %}

dbt resolves macros from your project namespace first. Adding both default__ and duckdb__ covers adapter dispatch for DuckDB and any fallback adapter.

Error 2: DuckDB file lock conflict with DBeaver

Running dbt run while DBeaver has the same .duckdb file open produces:

IO Error: Could not set lock on file "tuva.duckdb": Resource temporarily unavailable

DuckDB 1.10 enforces a single-writer model. Only one process can hold a write connection to a given database file at a time. DBeaver opens connections in read-write mode by default and holds that connection open as long as the client is running.

Fix: in DBeaver’s connection settings for the DuckDB connection, add the property:

read_only = true

DBeaver will still execute SELECT queries and browse schema, but it won’t acquire the write lock. dbt run then proceeds normally.

Error 3: DuckDB internal assertion failures

Two models fail with internal errors rather than SQL errors:

hcc_recapture__int_gap_status
quality_measures__int_adh_statins_numerator

The error message looks like:

Internal Error: Assertion failed (binder.cpp): column reference 'group_last' could not be resolved

Root cause: both models use complex CTEs where one CTE defines a window function that produces a column alias (group_last, best_past_rank), and a subsequent CTE references that alias inside another window’s ORDER BY or PARTITION BY clause. DuckDB 1.10 fails to bind these cross-CTE column references correctly.

This is a DuckDB 1.10 regression. The same models run cleanly on DuckDB 1.8.

Workarounds:

Final run result

After applying the limit_zero fix and excluding the two assertion failures:

875 PASS, 4 ERROR, 0 WARN

The four errors are all DuckDB 1.10 assertion failures — not logic errors, not data issues. All HEDIS quality measures (except the statin adherence numerator), all HCC suspecting marts, CCSR, and encounter marts run cleanly.

Summary

IssueRoot causeFix
limit_zero undefinedMacro resolution doesn’t cross package namespacesAdd local limit_zero.sql macro
DuckDB lock conflictDBeaver holds write lock by defaultSet DBeaver connection to read_only = true
Assertion failuresDuckDB 1.10 window CTE column binding regressionExclude models or downgrade DuckDB

Share this post:

Related Posts


Previous Post
Predicting patient risk with scikit-learn on top of HCC suspecting data
Next Post
The limit_zero macro bug: how dbt resolves macros across packages