What you will be able to do
- Explain which job runs feed dbt Catalog, and why a model can be missing from it or vanish from it
- Use the lineage graph, selectors and column-level lineage to trace where a breakage started and what it affects
- Read data health signals to tell a Caution resource from a Degraded one
- Use Model performance and Cost Insights to find slow, failing or expensive models and judge what the cost figures mean
Key concept
Catalog reflects deployed state — Catalog shows only what production or staging job runs have reported through the Discovery API. You read lineage, health, performance and cost there, so an environment that has not run, or a model that has not been built there, does not show up.
1.Where Catalog's picture of a project comes from
dbt Catalog is where you go to see the lineage, health and cost of a dbt project. It only shows what deployment job runs have reported. Catalog reads metadata from the Discovery API for the environment you have designated as production or staging. After each job run in that environment, it picks up the new metadata automatically, and that includes deploy jobs and merge jobs. Setting up a job to produce this documentation is covered in [generating documentation on a job](lesson:4.7). On dbt v2, jobs refresh catalog metadata on every run without a separate dbt docs generate step.
CI jobs never update Catalog. They test proposed changes rather than the production state, so they don't provide the metadata Catalog needs. A new model that has only passed CI won't appear in Catalog. It shows up after the next production or staging run that includes it.
Two conditions decide whether a model gets metadata at all. First, the model must exist in the manifest. Second, it must appear in the run_results of a dbt build, dbt run or dbt clone. It doesn't matter whether that run succeeded or failed. Because a failed build still counts, Catalog can show you the failure in context instead of hiding the model.
Metadata also disappears in two ways. If you delete a model from the project, its metadata goes after the next job run that no longer includes it. If an environment has no job runs for three months, all metadata for that environment is purged. For example, a staging environment that only runs before quarterly releases can turn up empty. Scheduling at least one job every three months prevents this.
| To see in Catalog | The job must run |
|---|---|
| All metadata | dbt build and dbt source freshness together in the same job |
| Model lineage, details, or results | dbt run or dbt build on that model |
| Columns and statistics | Any job run in the environment |
| Data test results | dbt test or dbt build |
| Source freshness results | dbt source freshness |
| Snapshot details | dbt snapshot or dbt build |
Checkpoint 1 of 8· Check yourself
An engineer opens a PR that adds fct_refunds. The CI job builds it successfully, but fct_refunds is not in Catalog. What explains this?
Catalog only reads metadata from production or staging deployment runs, such as deploy and merge jobs. CI runs don't represent the production state, so they are ignored.
“Note that CI jobs don't update Catalog. This is because they don't reflect the production state and don't provide the necessary metadata updates.”Source: docs.getdbt.com
Sources1
2.Reading the lineage graph to trace cause and impact
The lineage graph is an interactive version of the project's DAG. By default it shows applied state: models that have been built successfully and are available to query, not every model defined in code. This matters when you're troubleshooting. A model that is missing from the graph has not been built in that environment, even if it is in your branch.
To open the full graph, select Overview in the left sidebar and click Explore Lineage. If nothing appears, click Render Lineage. Very large projects may not render at all. In that case, select a subset of nodes with selectors instead of loading the whole DAG. Nodes are colour-coded and have icons by resource type. Tests and macros are left out of the full graph unless a search returns them, so you see their lineage on their own resource details pages.
For troubleshooting, the right-click menu is the main tool. You can refocus on a node with both directions, with downstream nodes only, or with upstream nodes only. When a model fails, refocus upstream to look for the cause and downstream to see what is affected. Selecting a node opens a side panel. Its General tab shows the description and materialization, and it has buttons to view the resource details, open the model in the Studio IDE, or copy a link to share.
The search bar takes the same selectors and graph operators you use on the command line. +orders returns everything upstream of orders. +dim_customers,resource_type:source returns only the sources upstream of dim_customers. You can also use --select and --exclude. Selectors that need a state comparison are not available, which means result, source status and state. To find what changed, use the Latest updates view, not a state selector.
Checkpoint 2 of 8· Check yourself
Which of these will NOT work in the Catalog search bar?
Catalog supports selectors and graph operators, but not the ones that need a state comparison: result, source status and state.
“All selectors are available for use, except those requiring a state comparison (result, source status, and state).”Source: docs.getdbt.com
Checkpoint 3 of 8· Exam question
In dbt Catalog, an analyst reviewing project_b's lineage graph sees that model downstream_c directly references the public model upstream_b from project_a. Model upstream_b itself depends on another public model, upstream_a, that also lives in project_a. The analyst reports that upstream_a is missing from downstream_c's lineage graph in Catalog, even though the Studio IDE lineage view shows both upstream_b and upstream_a. What explains this difference?
Correct answer: A — Catalog's lineage graph only shows public models that a project directly references, so upstream_a stays hidden because downstream_c only reaches it indirectly through upstream_b.
- A. This is correct. Catalog is designed to render only the public models a project directly references in its DAG, so an indirectly consumed model like upstream_a never appears there even though the Studio IDE dynamically resolves and displays the full dependency chain.
- B. This is incorrect. The gap is not a caching or staleness issue that resolves itself on the next run; Catalog intentionally omits indirectly referenced public models by design, so rerunning the production job would not make upstream_a appear.
- C. This is incorrect. Missing project permissions would prevent the analyst from viewing any of project_a's public models at all, not selectively hide only the indirectly referenced one while still showing upstream_b.
- D. This is incorrect. Contract enforcement governs whether a ref to a public model builds successfully and whether column checks run; it does not control which nodes are rendered in the Catalog lineage graph.
Sources1
3.Column-level lineage for pinpointing breakages
Model-level lineage shows which models depend on each other. Column-level lineage (CLL) narrows that down to the column. For each column in a model, source or snapshot, it traces where the data came from and where it goes next. In Catalog it needs an Enterprise or Enterprise+ plan and no setup. Open it from the column card on the Columns tab of a resource's details page. It is updated after every production or staging run.
A failing data test is a typical case. A not_null failure on a column in a mart model may come from a column several models upstream that has no test. CLL lets you follow that column back to the exact point where the problem started, so you don't have to read every model in between.
The column evolution lens tells you whether each step transformed the column or reused it as a Passthrough or Rename. Only transformed steps can introduce a bug, so this narrows down where to look. The Transformation column previews the formula that produced the value. Reused columns also inherit their description from upstream, as far back as the lineage goes. A description on a mart column may therefore have been written on a staging model, and that is where you edit it.
CLL is built by parsing SQL, which limits what it can see:
- It reflects select statements only, not joins or filters.
- SQL that is ambiguous or too complex, such as complex lateral joins, causes a parsing error.
- Python models can't be parsed, so they show a Python error.
- Hardcoded table names instead of ref can cause an unknown error.
In these cases the lineage may be incomplete, and Catalog shows a warning. To see the error details, expand the column's lineage graph and select the node.
Checkpoint 4 of 8· Check yourself
Which statement about column-level lineage in Catalog is wrong?
CLL follows columns through select statements only. Joins and filters are not represented.
“It doesn't reflect other usage like joins and filters.”Source: docs.getdbt.com
Sources2
4.Data health signals as a first triage step
Before you trace lineage, data health signals tell you which resources need attention. Models, sources and exposures each get one of four states: Healthy, Caution, Degraded or Unknown. The signal appears in search results, in the Health column of resource pages and public model tables, and in the node details panel of the lineage graph. For sources it also shows freshness status.
For triage, the useful distinction is between Caution and Degraded. Caution means something is missing or warning: no tests, a missing description, no build in 30 days, or an upstream freshness warning. Degraded means something is broken: the build failed, a test failed, or an upstream freshness check errored or hasn't run in 30 days. A model with no tests at all is never Degraded, which is one reason teams find untested models late.
| State | Triggered by |
|---|---|
| Healthy | Built successfully in last run and in last 30 days, tests configured and all passed, upstream sources fresh, has a description |
| Caution | Not built in 30 days, no tests, tests return warnings, upstream freshness warning, or missing description |
| Degraded | Model failed to build, failing tests, or upstream freshness check returned an error or has not run in 30 days |
| Unknown | No job runs have processed the resource |
Checkpoint 5 of 8· Match them up
Match each model health state to a condition that produces it
Tap a term, then the definition that fits it.
Warnings and gaps give Caution. Failures and freshness errors give Degraded. No processing at all gives Unknown.
“Unable to determine health of resource; no job runs have processed the resource.”Source: docs.getdbt.com
Checkpoint 6 of 8· Exam question
A data engineer opens the Columns tab for a model in dbt Catalog and finds a warning that the column-level lineage graph is incomplete for two of the model's source columns. Which two situations are documented causes of this kind of column-lineage parsing gap? (Select two.)(Select 2)
Correct answers: A, B — The model is written in Python, and dbt Catalog's column-level lineage parser cannot analyze Python model code the way it analyzes compiled SQL select statements.; The upstream select statement unpacks a JSON column into new fields, which is a pattern the SQL parser cannot resolve into individual column-level lineage edges.
- A. This is correct. Column-level lineage parses compiled SQL select statements, and Python model code falls outside what that parser can analyze, so Python-model columns surface as a documented parsing gap.
- B. This is correct. Unpacking a JSON column into new fields is one of the documented SQL patterns the lineage parser cannot resolve into clean column-to-column edges, alongside lateral joins and other complex SQL.
- C. This is incorrect. Column-level lineage is derived from the compiled select logic in a production or staging run, not from whether data tests executed, so choosing `dbt build` over `dbt run` does not by itself cause a lineage gap.
- D. This is incorrect. A missing column description only affects whether a description is inherited and displayed; it does not prevent the lineage parser from tracing that column's transformation.
Sources3
5.Finding slow, failing and expensive models
Model performance turns run history into a list of what to fix. It is available on Enterprise and Enterprise+. In Catalog, select your project and open the Performance tab. The overview ranks every model three ways: most executed (total count), longest average execution time, and most failures, split into run failures and test failures. You can filter by environment and job type. History goes back up to three months, and the default view is the last two weeks.
Each ranking calls for a different fix. A model that is slow but runs once a day is a refactoring candidate. A model that is quick but runs in every hourly job may be a job configuration problem, because it is rebuilt more often than its data changes. A model with a high failure rate is a reliability problem before it is a cost problem.
Each model's details page has its own Performance tab. Build time shows the average and its trend, Build count shows how often the model was built or reused, including failures, Test results shows pass and fail rates, and Consumption queries shows who queries it downstream. Click a data point on the Build time or Build count chart to see that day's job runs. Each row links to the run details, so a sudden jump in build time can be traced to the run that caused it.
Checkpoint 7 of 8· Put it in order
Put the steps for reaching a single model's cost and performance data in order
- 1.Navigate to the model, by search or via Models under Project assets
- 2.Click your project from the file tree
- 3.Open the Performance tab on the model's details page
- 4.From the main menu, go to Catalog
Model-level cost and performance live on the model's details page. You reach it through Catalog, then the project, then the model.
“Go to the the Performance tab on the model's details page.”Source: docs.getdbt.com
Cost Insights adds warehouse cost to the same model page. You also find it on the project dashboard and the job details page. It works with Snowflake, BigQuery, Databricks and Amazon Redshift (preview). The Cost insights, Usage and Query run time tabs show each model's estimated spend over time. Show table breaks the figures down, the All and Jobs buttons switch between project totals and per-job figures, and Download exports a CSV.
| Warehouse | Usage unit |
|---|---|
| Snowflake | Credits |
| BigQuery | Slot hours or bytes scanned, combined into one number |
| Databricks | Databricks Units (DBUs) |
| Amazon Redshift Serverless | RPU-hours |
| Amazon Redshift Provisioned | Node-hours |
These figures are estimates for optimisation, not for reconciling your bill. They can differ from the bill for several reasons:
- Snowflake doesn't attribute cost to queries that run in about 100 milliseconds or less, so totals can be lower than the Snowflake billing dashboard.
- Redshift shows $0 until you configure rpu_price_per_hour or node_price_per_hour.
- Redshift prices each query as if it ran alone, so concurrent queries can add up to more than the bill.
Cost reduction is calculated as average_cost_per_build * reuse_count, counting models that dbt State or state-aware orchestration reused instead of rebuilding. If those features were enabled before Cost Insights, it needs a full build within the last 10 days as a baseline. If no reduction figures appear, run a full build.
Checkpoint 8 of 8· Exam question
In dbt Catalog's multi-project view, an analyst clicks a public model that belongs to an upstream project and a details panel opens with General, Columns, and Relationships tabs. The analyst wants to find every downstream project currently consuming this specific public model. Which part of the panel should they open?
Correct answer: A — The Relationships tab, since it lists the downstream projects that reference the model under a 'Referenced by' section.
- A. This is correct. The Relationships tab is where Catalog surfaces the 'Referenced by' list of downstream projects and models currently consuming that public model.
- B. This is incorrect. The General tab shows descriptive metadata like the producing project, relation, and package, but it does not enumerate the downstream consumers of the model.
- C. This is incorrect. The Columns tab exposes column names, types, and column-level lineage within the model's own project scope; it is not where downstream project consumption is listed.
- D. This is incorrect. Account lineage is a separate account-wide view of project-to-project dependencies reached from Catalog's navigation, not a control embedded inside this model's details panel.
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.A successful CI run refreshes Catalog, so new models appear there as soon as the PR's CI job passes.Why is that wrong?
Only production and staging runs, including deploy and merge jobs, update Catalog. CI runs are ignored.
2.The default lineage graph shows every model defined in the project.Why is that wrong?
By default it shows applied state: models that have been built and are available to query.
Covered in Reading the lineage graph to trace cause and impact
3.Column-level lineage shows how a column is used in joins and filters, so it fully explains row-level discrepancies.Why is that wrong?
CLL follows columns through select statements only. Joins and filters are not reflected.
4.Cost Insights totals should match the warehouse invoice, so a gap means misconfiguration.Why is that wrong?
The figures are estimates for visibility and optimisation. Snowflake skips very short queries, and Redshift prices each query as if it ran alone.
Covered in Finding slow, failing and expensive models
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“The model appears in the run_results of a dbt build, dbt run, or dbt clone command, regardless of the run's success or failure status.”
↩︎ Where Catalog's picture of a project comes from“If an environment has had no job runs in the past 3 months, all metadata for that environment is purged.”
↩︎ Where Catalog's picture of a project comes from“The graph of very large projects might not render so you can select a subset of nodes by using selectors, instead.”
↩︎ Reading the lineage graph to trace cause and impact“By default, Catalog excludes these resources from the full lineage graph unless a search query returns them as results.”
↩︎ Reading the lineage graph to trace cause and impact“Catalog uses the metadata provided by the Discovery API to display the details about the state of your dbt project.”
↩︎ Key concept“Note that CI jobs do not update Catalog.”
↩︎ Exam trap 1“it shows models that have been successfully built and are available to query, not just the models defined in the project.”
↩︎ Exam trap 2“Note that CI jobs don't update Catalog. This is because they don't reflect the production state and don't provide the necessary metadata updates.”
↩︎ Checkpoint“By default, Catalog shows the project's applied state lineage.”
↩︎ Prediction“All selectors are available for use, except those requiring a state comparison (result, source status, and state).”
↩︎ Checkpoint - 2.
“a failing data test on a particular column in your dbt model might've stemmed from an untested column upstream.”
↩︎ Column-level lineage for pinpointing breakages“You can access CLL from the column card in the Columns tab in the Catalog resource details page for a model, source, or snapshot.”
↩︎ Column-level lineage for pinpointing breakages“A reused column, labeled as Passthrough or Rename in the lineage, automatically inherits its description from the source and upstream model columns.”
↩︎ Column-level lineage for pinpointing breakages“Column-level lineage reflects the lineage from select statements in your models' SQL code.”
↩︎ Exam trap 3“It doesn't reflect other usage like joins and filters.”
↩︎ Checkpoint - 3.
“using the indicators Healthy, Caution, Degraded, or Unknown.”
↩︎ Data health signals as a first triage step“For sources, the data health signal also indicates the source freshness status.”
↩︎ Data health signals as a first triage step“Unable to determine health of resource; no job runs have processed the resource.”
↩︎ Checkpoint - 4.
“displays the longest-running models, those most frequently executed, and the ones with the highest failure rates during runs/tests.”
↩︎ Finding slow, failing and expensive models“Select the time horizon using the filter, which defaults to a two-week lookback.”
↩︎ Finding slow, failing and expensive models“Each row in the table provides a direct link to the run details if you want to investigate further.”
↩︎ Finding slow, failing and expensive models - 5.https://docs.getdbt.com/docs/explore/cost-insightsOfficial docs
“Costs will appear as $0 until you configure rpu_price_per_hour (serverless) or node_price_per_hour (provisioned) in Cost Insights settings.”
↩︎ Finding slow, failing and expensive models“Snowflake doesn't attribute cost to queries that run in roughly 100 milliseconds or less.”
↩︎ Finding slow, failing and expensive models“Cost estimates are intended for visibility and optimization, not billing reconciliation.”
↩︎ Exam trap 4 - 6.
“at least one full model build must occur within the last 10 days to establish a baseline for cost reduction calculations.”
↩︎ Finding slow, failing and expensive models“Go to the the Performance tab on the model's details page.”
↩︎ Checkpoint