What you will be able to do
- Tell apart target/compiled, target/run and logs/dbt.log, and know which one to open for a given failure
- Use dbt compile (with --select or --inline) and dbt show to see the SQL a model or a Jinja snippet really produces
- Decide whether a failure is a plain SQL mistake or a Jinja/macro problem by comparing the model file with its compiled SQL
- Find the SQL behind failing tests and behind queries dbt runs on its own, such as merges and hooks
Key concept
Compiled SQL — Compiled SQL is your model after dbt has rendered every ref, macro and Jinja expression into plain SQL that the warehouse can run. When a model fails, the compiled SQL is what the warehouse actually received, so you debug that version and not the Jinja you wrote.
1.Where dbt writes the SQL it runs
The warehouse never sees the .sql file you wrote. It sees what dbt produces after rendering every {{ ref() }}, macro call and {% if %} block. Once a model mixes Jinja into its SQL, your source file and the query that ran are two different texts. Most hard bugs live in the gap between them. dbt writes the rendered text to disk, and learning where it goes is the first troubleshooting skill.
The CLI writes to three places, and each holds a different artifact. target/compiled/ holds the bare select statement for each model, and you can paste it into any query editor. target/run/ holds the statement dbt actually executed to build the model, meaning the select wrapped in the create statement for its materialization. logs/dbt.log records every query dbt ran plus extra logging, and the newest errors are at the bottom. In the dbt platform you get the same information by clicking a model in the run output and opening Details.
| Location | What it contains | Open it when |
|---|---|---|
| target/compiled/ | Compiled select statements you can run in any query editor | You want to see what your Jinja and macros rendered to, or run the model's query by hand |
| target/run/ | The SQL dbt executes to build your models (compiled create statements) | A Database Error points you here, and you want the exact statement the warehouse rejected |
| logs/dbt.log | All the queries dbt runs, plus additional logging; recent errors at the bottom | The failing SQL is not your model's select, for example a hook or an incremental merge |
Checkpoint 1 of 8· Match them up
Match each location to what you will find there.
Tap a term, then the definition that fits it.
The compiled directory holds bare selects, the run directory holds the statements that built the models, and the log records everything dbt sent to the warehouse.
“The target/run/ directory for compiled create statements”Source: docs.getdbt.com
Checkpoint 2 of 8· Exam question
Which two statements correctly distinguish `target/compiled/` from `target/run/` when troubleshooting a failing model?(Select 2)
Correct answers: A, B — `target/compiled/` holds only the rendered `select` statement, the place to check that Jinja and `ref()`/`source()` resolution produced the SQL you expect.; `target/run/` holds the actual SQL dbt executed, including materialization DDL, hooks, and merge or insert statements, not just plain select logic.
- A. Correct: the compiled directory contains only the rendered select statement after Jinja templating and ref/source resolution, which makes it the natural place to confirm that macros and dependencies rendered the logic you intended.
- B. Correct: the run directory contains the full SQL dbt actually sent to the warehouse, which for table or incremental materializations includes DDL, hooks, and merge/insert/update statements beyond the plain select.
- C. Incorrect: compilation happens before execution, so a model can compile successfully and produce a file under `target/compiled/` even if the later warehouse execution step is what ultimately fails.
- D. Incorrect: `target/run/` is populated when dbt actually executes SQL against the warehouse, which requires a live connection; `dbt compile` alone never touches the run directory.
- E. Incorrect: the two directories are not identical in content, since only the run directory carries materialization-specific DDL and write statements for non-view materializations, and neither directory stores row counts.
Sources1
2.Producing compiled SQL on demand
You don't have to run a model and wait for it to fail before you can read its compiled SQL. dbt compile renders models, data tests, analyses and other resources into the target/ directory without materializing anything. It exists for two debugging jobs: looking at what complex Jinja or macro logic produced, and running a model's or test's underlying select by hand to find a bug.
For one node, or for a snippet you're unsure about, compile can be interactive. --select names a single node, and --inline takes an arbitrary piece of dbt-SQL. In both cases dbt prints the compiled SQL to the terminal and also writes it to target/. The --inline form is the quickest way to see what a ref resolves to: select * from {{ ref('raw_orders') }} comes back as select * from "jaffle_shop"."main"."raw_orders".
dbt compile --select "stg_orders"
dbt compile --inline "select * from {{ ref('raw_orders') }}"Compile and parse do different jobs. dbt parse reads and validates the project without a warehouse connection. The manifest it writes has no compiled SQL in it. dbt compile does need a connection, because many models can only be rendered after introspective queries run, such as filling the relation cache or resolving a macro like run_query or dbt_utils.get_column_values. As a result, compiled SQL for those resources can differ depending on the state of warehouse metadata at the moment you compile. Compiling is also never a required step before a build: dbt run and the other building commands compile on their own.
dbt show goes one step further than compile. It compiles a single model, test, analysis or --inline query, runs it, and prints the first five rows in the terminal. Nothing gets materialized. Use --limit n to change the row count, which also adds a SQL limit so the warehouse only processes that many rows. Use compile when you need the SQL text and show when you need the data that SQL returns. Like the other commands in [dbt's command set](lesson:1.4), show selects only one node.
Checkpoint 3 of 8· Check yourself
A teammate wants compiled SQL for every model in a project, and plans to run dbt parse on a laptop with no warehouse access and then read the manifest. What actually happens?
Parse works offline and writes a manifest, but rendering SQL needs the connection that dbt compile uses for introspective queries.
“Parsing a project is enough to produce a manifest, however, keep in mind that the written-out manifest won't include compiled SQL.”Source: docs.getdbt.com
Checkpoint 4 of 8· Exam question
An analytics engineer runs `dbt run` and sees: `Compilation Error in macro (macros/cents_to_dollars.sql) Reached EOF without finding a close tag for macro (searched from line 1)`. What is the most likely root cause, and what should they check first in the compiled output?
Correct answer: A — The macro file is missing its closing `{% endmacro %}` tag; since this is a parse-time failure, there is no useful file under `target/compiled/` for this macro to inspect.
- A. Correct: an unclosed Jinja tag is a parsing-stage compilation error, which happens before dbt can render any SQL for that node, so no compiled artifact exists yet for the broken macro; the fix is to locate and add the missing `{% endmacro %}` in the source file.
- B. Incorrect: a missing column produces a database error referencing the column name, not an 'unclosed tag' message, and that failure mode surfaces after successful compilation, not during Jinja parsing.
- C. Incorrect: an ambiguous column error is a warehouse-level database error that occurs after SQL has already compiled cleanly, which does not match a 'reached EOF' Jinja parsing failure at all.
- D. Incorrect: a circular reference produces a dependency-graph cycle error naming the looping models, which is a distinct error category from a Jinja tag that never closed inside a macro file.
3.Is it your SQL or your Jinja?
A Database Error means the warehouse rejected a query, and dbt passes the warehouse's message along. [Reading that message](lesson:3.1) tells you which model failed. It also gives a path, and that path points to compiled SQL, not to your model file. The line and position numbers in the error count lines in the compiled file. If your model has a long Jinja loop, "line 14" in the compiled file may be nowhere near line 14 of what you wrote.
Database Error in model customers (models/customers.sql)
001003 (42000): SQL compilation error:
syntax error line 14 at position 4 unexpected 'from'.
compiled SQL at target/run/jaffle_shop/models/customers.sqlMost of these failures are plain SQL mistakes in the model. To find them, open the model and its compiled SQL side by side and re-run the query by itself. On the CLI, paste the compiled query into a query runner such as the Snowflake UI. In the dbt platform, use the Preview button on the model. Once the isolated query runs cleanly, fix the model and rerun the failed model. Any models downstream of the failure were skipped, so they need to run again too. Testing that fix before merging is covered in [fix-then-test](lesson:3.4).
Checkpoint 5 of 8· Put it in order
A model fails with a Database Error. Put the troubleshooting steps in order.
- 1.Fix the mistake
- 2.Open the model file and its compiled SQL side by side
- 3.Re-execute the compiled query on its own to isolate the error
- 4.Rerun the failed model
You isolate the problem in the compiled query before changing anything, and only then rerun the model to confirm the fix.
“it can be useful to show these side-by-side in your code editor.”Source: docs.getdbt.com
The side-by-side comparison also catches the other kind of bug: SQL that dbt generated wrongly. If the compiled text doesn't match what you meant to write, the problem is in the Jinja. You might see a missing column, an empty where, or a loop that produced one branch too many. Jinja bugs often fail quietly. Pass a column name to a macro without quotes, and Jinja looks for a variable of that name. When none exists, the argument renders as nothing. The resulting SQL is broken, or worse, valid but wrong, and you'll only see why by reading target/compiled. To check what a variable holds while a model is rendering, the log function prints objects to the command line.
Checkpoint 6 of 8· Fill the gap
This macro call should convert the amount column. Which argument makes it compile to the intended SQL?
{{ cents_to_dollars( ? ) }} as amount_usdInside {{ }} a string argument needs quotes. Unquoted, amount is looked up as a variable, which doesn't exist, so it compiles to nothing. Nested curlies are not valid inside an expression either.
Source: docs.getdbt.comCheckpoint 7 of 8· Exam question
A teammate says `dbt run -s orders` keeps failing with what looks like the same error even after they edited `models/orders.sql` several times. Each proposed next action below is a complete, standalone step. Which single action is most likely to resolve the confusion?
Correct answer: A — Confirm the editor actually saved `models/orders.sql`, since dbt compiles whatever is on disk and an unsaved buffer reproduces the prior error unchanged.
- A. Correct: dbt reads and compiles the file contents on disk at invocation time, so an unsaved edit means the previous, still-broken version is what actually gets compiled and run again, which exactly matches 'same error after editing.'
- B. Incorrect: the materialization strategy controls how the result set is persisted (view vs. physical table) and has no bearing on whether dbt picks up unsaved source edits between invocations.
- C. Incorrect: `target/` is regenerated fresh from the current source files on every `dbt run`, so stale compiled artifacts are not the mechanism that would make repeated edits appear ineffective.
- D. Incorrect: `orders` was described only as a plain model being edited for a compilation or SQL error, not as an incremental model with a caching concern, so `--full-refresh` addresses a different problem than an unsaved file.
4.SQL you didn't write: hooks, merges and tests
Some Database Errors come from queries dbt runs on its own, so the model's compiled select will look fine. dbt also sends introspective queries that list objects, statements that create schemas, pre- and post-hooks, on-run-start and on-run-end hooks, and the merge, update and insert statements behind incremental models and snapshots. None of those is in your model's select. All of them are in logs/dbt.log, or in Details in the dbt platform.
A busy log is hard to read, so shrink it first. Clear the contents of logs/dbt.log, then run dbt run again for just the failing model. The file will then contain only the queries for that one failure, and the statement the warehouse rejected will be near the bottom.
Failing tests are debugged the same way. Every test compiles to a query that returns the failing records. On the CLI, the compiled test queries are in target/compiled/schema_tests. In the dbt platform, click the failed test and open Details. Paste that SQL into a query editor and run it, and you see exactly which rows broke the assertion. To produce test SQL without running anything, dbt compile --select "resource_type:test" compiles every test node.
Checkpoint 8 of 8· Check yourself
An incremental model fails with a Database Error, but its compiled select in target/compiled runs cleanly in the query editor. Where should you look next?
Incremental models run merge, update and insert statements that are not part of the compiled select. The log records every query dbt ran, including those.
“In these cases, you should check out the logs — this contains all the queries dbt has run.”Source: docs.getdbt.com
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.You must run dbt compile before dbt run so the compiled SQL exists.Why is that wrong?
Building commands do their own compilation. dbt compile is a debugging tool, not a step in the pipeline.
Covered in Producing compiled SQL on demand
2.A Jinja mistake such as an unquoted macro argument will always raise a compilation error.Why is that wrong?
An unquoted name is treated as a variable. If that variable doesn't exist, it renders as nothing, so the bug only shows up in the compiled SQL.
Covered in Is it your SQL or your Jinja?
3.Compiled SQL depends only on your project code, so compiling twice always gives the same result.Why is that wrong?
Resources that use introspective queries are rendered from warehouse metadata, so their compiled SQL can change as that metadata changes.
Covered in Producing compiled SQL on demand
4.If a model's compiled select runs cleanly, the Database Error can't have come from dbt.Why is that wrong?
dbt also runs hooks, schema creation, and incremental merge/insert statements, and any of these can fail. The log records them all.
Practise it for real
See the gap between a model's Jinja and the SQL dbt actually sends to the warehouse
1.Run dbt compile --inline "select * from {{ ref('raw_orders') }}" against a project that has a raw_orders node
Why: It shows what a ref resolves to without building anything
You should see: dbt logs the compiled SQL to the terminal, in addition to writing to the target/ directory
2.Run dbt compile --select for one of your Jinja-heavy models, then open its file under target/compiled
Why: The compiled select is the version you can paste into a query editor and run on its own
You should see: A plain select statement with every ref and macro rendered
3.Run dbt show --select for the same model
Why: It compiles the model again and runs it, so you see the rows your current code returns
You should see: The first 5 rows of the query result printed in the terminal
Stuck? Get a nudge
If the compiled SQL is not what you expected, the bug is in the Jinja, not in the SQL.
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.https://docs.getdbt.com/guides/debug-errorsOfficial docs
“The target/run directory contains the SQL dbt executes to build your models.”
↩︎ Where dbt writes the SQL it runs“The logs/dbt.log file contains all the queries that dbt runs, and additional logging.”
↩︎ Where dbt writes the SQL it runs“90% of the time, there's a mistake in the SQL of your model.”
↩︎ Is it your SQL or your Jinja?“Then, re-execute dbt run for just the problematic model. The logs will just have the output you're looking for.”
↩︎ SQL you didn't write: hooks, merges and tests“The target/compiled directory contains select statements that you can run in any query editor.”
↩︎ Key concept“For incremental models, and snapshots: merge, update and insert statements”
↩︎ Exam trap 4“it can be useful to show these side-by-side in your code editor.”
↩︎ Checkpoint“In these cases, you should check out the logs — this contains all the queries dbt has run.”
↩︎ Checkpoint - 2.https://docs.getdbt.com/reference/commands/compileOfficial docs
“Visually inspecting the compiled output of resource files. This is useful for validating complex Jinja logic or macro usage.”
↩︎ Producing compiled SQL on demand“This will log the compiled SQL to the terminal, in addition to writing to the target/ directory.”
↩︎ Producing compiled SQL on demand“dbt compile is not a pre-requisite of dbt run, or other building commands.”
↩︎ Exam trap 1“Compiled SQL for resources that use introspective queries may depend on metadata from your warehouse.”
↩︎ Exam trap 3“Parsing a project is enough to produce a manifest, however, keep in mind that the written-out manifest won't include compiled SQL.”
↩︎ Checkpoint - 3.https://docs.getdbt.com/reference/commands/showOfficial docs
“If previewing a model, dbt will always compile and run the compiled query from source.”
↩︎ Producing compiled SQL on demand - 4.
“Use the error message and the compiled SQL to debug any errors.”
↩︎ Is it your SQL or your Jinja? - 5.https://docs.getdbt.com/faqs/Project/debugging-jinjaOfficial docs
“You can also use the log function to debug Jinja by printing objects to the command line.”
↩︎ Is it your SQL or your Jinja? - 6.https://docs.getdbt.com/faqs/Runs/failed-testsOfficial docs
“Navigate to the target/compiled/schema_tests directory for all compiled test queries.”
↩︎ SQL you didn't write: hooks, merges and tests
Also cited
“Without the quotes, the Jinja parser will look for a variable named amount. Since this doesn't exist, it will compile to nothing.”
↩︎ Exam trap 2“The target/run/ directory for compiled create statements”
↩︎ Checkpoint