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
- Read the `(from, to)` pairs in the error and delete the matching direct `.in_set` edge(s)
- Declare each containment relationship in exactly one place (the set's owning plugin)
- 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
- Declare nesting once per relationship
- Keep hierarchy declarations colocated with set definitions
- Escalate to LogLevel::Error in CI so redundant edges never accumulate silently
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
- DAG has a cross-dependency between nodes {0:?} and {1:?}
- DAG has overlapping groups between keys {0:?} and {1:?}
- HierarchySort
- DependencySort
- FlatDependencySort
AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20).
Data as JSON: /api/errors/b6f70b558d39a474.
Report an issue: GitHub.