bevyengine/bevy · critical

Error when initializing schedule {:?}: {}

Error message

Error when initializing schedule {:?}: {}

What it means

Schedule::run initializes the schedule before executing it; if building the executable schedule fails (ordering cycles, self-loops, ambiguous SystemTypeSet ordering, other build errors), run() panics with this wrapper message. The text after the colon embeds the underlying ScheduleBuildError rendered against the graph and world, naming the offending nodes/edges. It fires on the first run of the schedule, typically the first frame.

Source

Thrown at crates/bevy_ecs/src/schedule/schedule.rs:575

    }

    /// Set whether the schedule applies deferred system buffers on final time or not. This is a catch-all
    /// in case a system uses commands but was not explicitly ordered before an instance of
    /// [`ApplyDeferred`]. By default this
    /// setting is true, but may be disabled if needed.
    pub fn set_apply_final_deferred(&mut self, apply_final_deferred: bool) -> &mut Self {
        self.executor.set_apply_final_deferred(apply_final_deferred);
        self
    }

    /// Runs all systems in this schedule on the `world`, using its current execution strategy.
    pub fn run(&mut self, world: &mut World) {
        #[cfg(feature = "trace")]
        let _span = info_span!("schedule", name = ?self.label).entered();

        world.check_change_ticks();
        self.initialize(world).unwrap_or_else(|e| {
            panic!(
                "Error when initializing schedule {:?}: {}",
                self.label,
                e.to_string(self.graph(), world)
            )
        });

        let error_handler = world.fallback_error_handler();

        #[cfg(not(feature = "bevy_debug_stepping"))]
        self.executor
            .run(&mut self.executable, world, None, error_handler);

        #[cfg(feature = "bevy_debug_stepping")]
        {
            let skip_systems = match world.get_resource_mut::<Stepping>() {
                None => None,
                Some(mut stepping) => stepping.skipped_systems(self),
            };

View on GitHub (pinned to 396ca72708)

Solutions

  1. Read the embedded detail after the colon - it identifies the exact nodes and constraint at fault; fix that underlying build error.
  2. Reproduce as a failing test: call app.update() once right after configuring systems so CI catches it before runtime.
  3. If you need the error instead of a panic, call schedule.initialize(&mut world) yourself and handle the Result before calling run().

Example fix

// before
schedule.run(&mut world); // panics on invalid graph

// after
if let Err(e) = schedule.initialize(&mut world) {
    log::error!("bad schedule: {}", e.to_string(schedule.graph(), world));
    return;
}
schedule.run(&mut world);
Defensive patterns

Strategy: try-catch

Validate before calling

// Fail fast in a test instead of frame 1
#[test]
fn app_boots() {
    let mut app = App::new();
    app.add_plugins(DefaultPlugins);
    app.update();
}

Try / catch

if let Err(e) = schedule.initialize(&mut world) {
    log::error!("schedule init failed: {}", e.to_string(schedule.graph(), world));
    return; // skip running instead of letting run() panic
}
schedule.run(&mut world);

Prevention

When it happens

Trigger: Any ScheduleBuildError surfacing through Schedule::run: a cycle or self-loop introduced by new ordering constraints, ordering against a duplicated system type, or graph inconsistencies added since the last successful run.

Common situations: App panics on frame 1 after adding a plugin or ordering constraint; systems/sets configured differently between test and app so a constraint only loops in one of them; large refactors of schedule configuration.

Related errors


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