bevyengine/bevy · error · ScheduleBuildError

CrossDependency

CrossDependency

Error message

`{:?}` and `{:?}` have both `in_set` and `before`-`after` relationships (these might be transitive). This combination is unsolvable as a system cannot run before or after a set it belongs to.

What it means

`CrossDependency` means a node (system or set) has both an `in_set` relationship and a `before`/`after` relationship with the same set, possibly transitively. A system cannot be ordered strictly before or after a set it belongs to — every member ordering includes itself — so the constraint set is unsolvable and schedule initialization fails.

Source

Thrown at crates/bevy_ecs/src/schedule/error.rs:33

    },
    world::World,
};

/// Category of errors encountered during [`Schedule::initialize`](crate::schedule::Schedule::initialize).
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum ScheduleBuildError {
    /// Tried to topologically sort the hierarchy of system sets.
    #[error("Failed to topologically sort the hierarchy of system sets: {0}")]
    HierarchySort(DiGraphToposortError<NodeId>),
    /// Tried to topologically sort the dependency graph.
    #[error("Failed to topologically sort the dependency graph: {0}")]
    DependencySort(DiGraphToposortError<NodeId>),
    /// Tried to topologically sort the flattened dependency graph.
    #[error("Failed to topologically sort the flattened dependency graph: {0}")]
    FlatDependencySort(DiGraphToposortError<SystemKey>),
    /// Tried to order a system (set) relative to a system set it belongs to.
    #[error("`{:?}` and `{:?}` have both `in_set` and `before`-`after` relationships (these might be transitive). This combination is unsolvable as a system cannot run before or after a set it belongs to.", .0.0, .0.1)]
    CrossDependency(#[from] DagCrossDependencyError<NodeId>),
    /// Tried to order system sets that share systems.
    #[error("`{:?}` and `{:?}` have a `before`-`after` relationship (which may be transitive) but share systems.", .0.0, .0.1)]
    SetsHaveOrderButIntersect(#[from] DagOverlappingGroupError<SystemSetKey>),
    /// Tried to order a system (set) relative to all instances of some system function.
    #[error(transparent)]
    SystemTypeSetAmbiguity(#[from] SystemTypeSetAmbiguityError),
    /// Tried to run a schedule before all of its systems have been initialized.
    #[error("Tried to run a schedule before all of its systems have been initialized.")]
    Uninitialized,
    /// A warning that was elevated to an error.
    #[error(transparent)]
    Elevated(#[from] ScheduleBuildWarning),
}

/// Category of warnings encountered during [`Schedule::initialize`](crate::schedule::Schedule::initialize).
#[non_exhaustive]
#[derive(Error, Debug)]

View on GitHub (pinned to 396ca72708)

Solutions

  1. Order against a sibling instead of an enclosing set: point `.before`/`.after` at the specific system or a sibling set, not one that contains the constrained node
  2. If the member genuinely must bracket the set, split the set so the constrained member moves outside it
  3. Audit combined in_set + before/after configuration, including edges contributed by multiple plugins transitively

Example fix

// before
app.add_systems(Update, cleanup.in_set(SimSet).after(SimSet)); // cross-dependency

// after
app.add_systems(Update, cleanup.after(sim_tick).after(sim_apply)); // order against members
// or move cleanup out of SimSet
Defensive patterns

Strategy: try-catch

Validate before calling

// Before configuring, keep an explicit model of set membership and check:
// never order a node against a set that (transitively) contains it
fn contains(hierarchy: &SetHierarchy, set: SetId, maybe_descendant: SetId) -> bool { /* walk edges */ false }

Try / catch

match schedule.initialize(&mut world) {
    Err(ScheduleBuildError::CrossDependency(DagCrossDependencyError(a, b))) => {
        // a is both inside and ordered against b: repoint the ordering at a sibling
    }
    other => other.map(|_| ()),
}

Prevention

When it happens

Trigger: `.add_systems(s, my_system.in_set(MySet).before(MySet))`; or configuring `A.before(B)` where B contains A through a chain of `in_set` edges added by different plugins; run-conditions tuples that implicitly nest sets.

Common situations: Trying to pin one member of a set relative to the whole set ('run this cleanup after everything in Simulation'); plugin A ordering against plugin B's set while also adding its systems into that set; transitive containment making an apparently-legal ordering illegal.

Related errors


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