bevyengine/bevy · warning · DagRedundancyError

DAG has redundant edges: {0:?}

Error message

DAG has redundant edges: {0:?}

What it means

`DagRedundancyError` is the low-level graph error carrying the list of redundant edges as `(N, N)` pairs — edges whose target is already reachable through a longer path. Bevy's schedule builder surfaces it wrapped as `ScheduleBuildWarning::HierarchyRedundancy` when checking the system-set hierarchy; the Vec contents tell you exactly which containment edges to delete.

Source

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

impl<K, V, S> Default for DagGroups<K, V, S>
where
    S: BuildHasher + Default,
{
    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};

View on GitHub (pinned to 396ca72708)

Solutions

  1. Read the `(from, to)` pairs in the error and delete the matching direct `.in_set` edge(s)
  2. Declare each containment relationship in exactly one place (the set's owning plugin)
  3. If intentional, downgrade the check via `ScheduleBuildSettings::hierarchy_detection = LogLevel::Ignore`

Example fix

// before
app.configure_sets(Update, (A, B.in_set(A), C.in_set(B).in_set(A)));
// DagRedundancyError([(C, A)]) wrapped in HierarchyRedundancy

// after
app.configure_sets(Update, (A, B.in_set(A), C.in_set(B)));
Defensive patterns

Strategy: fallback

Validate before calling

// Inspect hierarchy edges for redundancy before initialize by walking your own set-config model
// (Bevy surfaces it post-hoc); or pre-configure tolerance:
settings.hierarchy_detection = LogLevel::Ignore;

Try / catch

match schedule.initialize(&mut world) {
    Err(ScheduleBuildError::Elevated(ScheduleBuildWarning::HierarchyRedundancy(DagRedundancyError(edges)))) => {
        // edges: Vec<(NodeId, NodeId)> — delete the corresponding direct .in_set declarations
    }
    _ => {}
}

Prevention

When it happens

Trigger: Building a schedule whose set hierarchy declares an edge that transitive `in_set` chains already imply — the same condition that produces `HierarchyRedundancy`; you see this raw type when matching on the warning's inner error or in lower-level Dag APIs.

Common situations: Plugins re-declaring nesting that another plugin already established; copy-pasted configure_sets blocks; refactors that shorten one hierarchy path while leaving the old shortcut edge.

Related errors


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