bevyengine/bevy · error · DagCrossDependencyError

DAG has a cross-dependency between nodes {0:?} and {1:?}

Error message

DAG has a cross-dependency between nodes {0:?} and {1:?}

What it means

`DagCrossDependencyError` is the low-level graph error stating that nodes `{0}` and `{1}` carry dependencies in both directions between the hierarchy and dependency graphs — concretely, a `before`/`after` edge between a node and a set that contains it. Bevy wraps it into `ScheduleBuildError::CrossDependency` during schedule initialization; the two node ids identify the conflicting pair.

Source

Thrown at crates/bevy_ecs/src/schedule/graph/dag.rs:719

    fn default() -> Self {
        Self(Default::default())
    }
}

impl<K: Debug, V: Debug, S> Debug for DagGroups<K, V, S> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("DagGroups").field(&self.0).finish()
    }
}

/// Error indicating that the graph has redundant edges.
#[derive(Error, Debug)]
#[error("DAG has redundant edges: {0:?}")]
pub struct DagRedundancyError<N: GraphNodeId>(pub Vec<(N, N)>);

/// Error indicating that two graphs both have a dependency between the same nodes.
#[derive(Error, Debug)]
#[error("DAG has a cross-dependency between nodes {0:?} and {1:?}")]
pub struct DagCrossDependencyError<N>(pub N, pub N);

/// Error indicating that the graph has overlapping groups between two keys.
#[derive(Error, Debug)]
#[error("DAG has overlapping groups between keys {0:?} and {1:?}")]
pub struct DagOverlappingGroupError<K>(pub K, pub K);

#[cfg(test)]
mod tests {
    use core::ops::DerefMut;

    use crate::schedule::graph::{index, Dag, Direction, GraphNodeId, UnGraph};

    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
    struct TestNode(u32);

    impl GraphNodeId for TestNode {
        type Adjacent = (TestNode, Direction);

View on GitHub (pinned to 396ca72708)

Solutions

  1. Repoint the ordering at a sibling system or set that does not contain the constrained node
  2. Move the constrained node out of the set if it truly must bracket the set's execution
  3. Trace which plugins contribute the `in_set` and `before` edges — the conflict is often between edges from two different sources

Example fix

// before
app.add_systems(Update, x.in_set(S).before(S)); // DagCrossDependencyError(x, S)

// after
app.add_systems(Update, x.in_set(S));
app.add_systems(Update, y.in_set(S).before(x)); // order inside the set instead
Defensive patterns

Strategy: try-catch

Validate before calling

// Guard at config time: never emit ordering vs a containing set
fn orderable(hierarchy: &SetHierarchy, node: SetId, target: SetId) -> bool {
    !hierarchy.is_descendant_of(target, node)
}

Try / catch

match schedule.initialize(&mut world) {
    Err(ScheduleBuildError::CrossDependency(err @ DagCrossDependencyError(_, _))) => {
        // err.0/err.1 name the node/set pair; repoint the .before/.after edge at a sibling
    }
    other => other.map(|_| ()),
}

Prevention

When it happens

Trigger: The same condition as `CrossDependency`: a system or set ordered `.before`/`.after` a set it (transitively) belongs to. You encounter this raw type when matching on the inner error of `CrossDependency` or using the Dag API directly.

Common situations: Bracketing constraints ('run X after everything in set S' where X is in S); plugin A ordering against plugin B's set while contributing systems into it; transitive in_set chains making a local ordering global and self-referential.

Related errors


AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20). Data as JSON: /api/errors/6ea4d8a460d6e282. Report an issue: GitHub.