ServicesAboutNotesContact Get in touch →
EN FR
Note

Debugging dbt with Claude Code

How to use Claude Code for dbt debugging — letting the agent face errors directly, tracing data issues through upstream models, and using subagents for complex investigations

Planted
dbtclaude codebigquerydata engineeringai

The effective approach with Claude Code is to let the agent face errors directly rather than pre-gathering evidence or pasting error messages for interpretation. Claude Code executes commands, reads files, and makes edits in a single loop: it runs dbt build, reads the error, checks the compiled SQL, reads upstream models if needed, applies a fix, and reruns. The debug cycle runs without manual step management.

Build Errors

For errors that prevent a model from building — SQL syntax issues, broken ref() calls, type mismatches, Jinja compilation failures:

Run dbt build --select mrt__sales__orders and fix any errors.

That’s it. Claude executes the command, reads the error output, and investigates. For a typical build error, Claude’s process looks like:

  1. Run dbt build --select mrt__sales__orders
  2. See the error (e.g., “Column amount not found in table”)
  3. Read the model SQL to find the reference
  4. Read the upstream model to check the actual column name
  5. Fix the reference (amountorder_amount)
  6. Run dbt build again to verify

Claude Code handles cross-file errors well: column renames in upstream models that break downstream references, macro argument changes that cascade, schema drift in source tables.

Data Issues: Wrong Results, Not Build Failures

Data issues are harder than build errors because the model runs successfully but produces wrong results. The approach is different — describe the symptom, not the suspected cause:

The mrt__sales__orders model shows incorrect total_revenue for customer_id='ABC123'.
Trace through the upstream models to find where the calculation goes wrong.

Claude’s investigation for a data issue is more exploratory:

  1. Query mrt__sales__orders for the problematic customer to confirm the issue
  2. Read the model SQL to understand the total_revenue calculation
  3. Identify upstream dependencies (ref() calls)
  4. Query intermediate tables to check values at each transformation step
  5. Find the divergence point (“the revenue is correct in int__orders__enriched but wrong in the mart — the aggregation double-counts returns”)
  6. Fix the logic and verify the output

The key principle: describe the problem, not the debugging steps. “total_revenue is wrong for this customer” gives Claude freedom to investigate however makes sense. “Check if there’s a duplicate join in CTE #3” constrains Claude to your hypothesis, which might be wrong.

Subagents for Complex Debugging

Some bugs are genuinely complex: data arriving late that corrupts incremental models, race conditions in concurrent dbt runs, subtle timezone issues that only manifest on certain dates. For these, a fresh context focused entirely on debugging helps.

Create a debugging subagent at .claude/agents/sql-debugger.md:

---
name: sql-debugger
description: Specialized agent for debugging SQL and dbt issues
---
You are an expert SQL debugger specializing in BigQuery and dbt.
## Your Approach
1. **Gather Evidence First**
- Read error logs completely
- Check compiled SQL
- Review recent git changes to the model
2. **Form Hypotheses**
- List possible causes ranked by likelihood
- Consider: data issues, schema changes, logic errors
3. **Test Systematically**
- Isolate the problem with minimal reproducing case
- Use WHERE clauses to test on small data
- Add CTEs to inspect intermediate results
4. **Fix and Verify**
- Implement the minimum change to fix the issue
- Add tests to prevent regression
- Document the root cause

Invoke it when your main conversation has become cluttered with other context:

Use the sql-debugger agent to investigate why int__ga4__sessions_attributed
is returning NULL for channel_grouping on 5% of sessions.

The subagent gets fresh context. It doesn’t carry the baggage of whatever else you were working on in the main conversation. This matters because Claude’s ability to focus degrades in very long conversations with mixed topics — the debugging investigation competes for attention with model-building, documentation, and other tasks.

When Subagents Help vs. Overkill

Use the main conversation for:

  • Build errors with clear error messages
  • Simple data issues with obvious symptoms
  • Errors in models you’re actively working on

Use subagents for:

  • Issues requiring investigation across 5+ files
  • Problems where you’ve already spent several conversation turns without resolution
  • Debugging sessions that need to coexist with other work in progress

The overhead of spinning up a subagent (it has to re-read context from scratch) isn’t worth it for a simple “column not found” error. It’s very worth it for “5% of sessions have NULL attribution and we don’t know why.”

Debugging Patterns That Work Well

“Fix and run” loops. Tell Claude to fix an error and re-run the build in one instruction. Claude handles the iteration internally, which is faster than you reviewing each fix before the next attempt.

Compiled SQL inspection. When a Jinja-heavy model produces unexpected results, ask Claude to read the compiled SQL in target/compiled/. The compiled version shows exactly what BigQuery received, stripping away the Jinja abstraction.

Git-aware debugging. “This model was working yesterday. Check what changed since then and identify what broke it.” Claude can run git diff on the model and its dependencies to find recent changes that correlate with the failure.

Upstream tracing with queries. For data issues, Claude can write and execute diagnostic queries at each step of the lineage chain. This is faster than manually writing queries because Claude reads the SQL and generates targeted SELECT statements that check specific values.

After a Debug Session

After Claude fixes a bug:

  1. Add a test that catches the specific failure. The dbt Testing Taxonomy covers which test type fits which scenario.
  2. Add a hook if the bug was caused by an operation that should have been blocked (editing a protected file, running without partition filters, etc.).
  3. Update CLAUDE.md if Claude repeatedly makes the same mistake that leads to bugs (using the wrong date function, forgetting timezone handling, etc.).

Each debugging session should leave behind a permanent defense. A test catches the regression. A hook prevents the action. A CLAUDE.md entry prevents the pattern. Over time, the same bugs stop recurring — not because you remember to avoid them, but because the system prevents them.