Metacenta

Lineage integrity · rule dependency-cycle

dbt dependency cycles in the model DAG

A Metacenta review checks this under the rule The model DAG is acyclic. Everything below applies whether or not you ever commission one.

What this rule checks

This rule flags each dependency cycle among the models in a dbt manifest: a model that depends on itself through its chain of ref() calls. We report each cycle once and name every model in it. A manifest with no cycle passes.

Why it matters

No build order can satisfy a cycle, so dbt cannot build the project at all. dbt refuses to compile a cyclic project, so a cycle in the manifest means it was edited by hand or produced by a fork of dbt.

How to fix it

Break the loop of models that depend on each other. Move the logic both models share into a new upstream model, and ref() that from each side. Then regenerate the manifest with dbt parse and check the cycle is gone.

Before:

-- int_orders.sql
select order_id, amount
from {{ ref('int_payments') }}

-- int_payments.sql
select order_id, amount
from {{ ref('int_orders') }}

After:

-- int_order_payments.sql: the shared logic
select order_id, amount
from {{ ref('stg_payments') }}

-- int_orders.sql
select order_id, amount
from {{ ref('int_order_payments') }}

-- int_payments.sql
select order_id, amount
from {{ ref('int_order_payments') }}

When it is fine to leave

None in a dbt project, because dbt will not compile one. If this fires, look first at how the manifest was produced. A manifest rewritten by a script, or built by a patched dbt, is the usual cause.

What we need to check it

manifest.json alone, which dbt parse writes. The rule declines when the manifest declares no models.