bevyengine/bevy · error · ScheduleError

ScheduleNotFound

ScheduleNotFound

Error message

Schedule not found.

What it means

`ScheduleError::ScheduleNotFound` is returned by `Schedules::remove_systems_in_set` (and sibling `Schedules` methods) when `get_mut(schedule)` finds no schedule under that label — the schedule was never created via `add_systems`/`entry` for that `ScheduleLabel`. It is the schedules-collection level of 'key not present', distinct from `SetNotFound` which is set-level within an existing schedule.

Source

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

            }
            ScheduleBuildWarning::Ambiguity(AmbiguousSystemConflictsWarning(ambiguities)) => {
                ScheduleBuildError::ambiguity_to_string(ambiguities, graph, world.components())
            }
        }
    }
}

/// Error returned from some `Schedule` methods
#[derive(Error, Debug)]
pub enum ScheduleError {
    /// Operation cannot be completed because the schedule has changed and `Schedule::initialize` needs to be called
    #[error("Operation cannot be completed because the schedule has changed and `Schedule::initialize` needs to be called")]
    Uninitialized,
    /// Method could not find set
    #[error("Set not found")]
    SetNotFound,
    /// Schedule not found
    #[error("Schedule not found.")]
    ScheduleNotFound,
    /// Error initializing schedule
    #[error("{0}")]
    ScheduleBuildError(ScheduleBuildError),
}

impl From<ScheduleBuildError> for ScheduleError {
    fn from(value: ScheduleBuildError) -> Self {
        Self::ScheduleBuildError(value)
    }
}

View on GitHub (pinned to 396ca72708)

Solutions

  1. Create/populate the schedule first: `schedules.add_systems(Label, ...)` or use `.entry(Label)` which inserts on demand
  2. Double-check the exact label type and that the plugin owning the schedule was added before your call
  3. Handle the `Err` gracefully in optional-cleanup paths instead of unwrapping

Example fix

// before
let n = schedules.remove_systems_in_set(MyLabel, MySet, &mut world, policy)?; // ScheduleNotFound

// after
schedules.entry(MyLabel).add_systems(dummy_placeholder_free_marker); // ensure schedule exists
// or simply verify first:
if schedules.get_mut(MyLabel).is_some() { /* ... proceed ... */ }
Defensive patterns

Strategy: try-catch

Validate before calling

// Check the schedule exists before operating on it
if schedules.get_mut(MyLabel).is_some() {
    schedules.remove_systems_in_set(MyLabel, MySet, &mut world, policy)?;
}
// or create on demand: schedules.entry(MyLabel);

Try / catch

match schedules.remove_systems_in_set(MyLabel, MySet, &mut world, policy) {
    Err(ScheduleError::ScheduleNotFound) => Ok(0), // nothing to clean
    other => other,
}

Prevention

When it happens

Trigger: Calling `schedules.remove_systems_in_set(MyLabel, set, ...)` when no schedule exists under `MyLabel`; using a different (or custom) label type than the one the systems were added under; removing before the plugin that creates the schedule has built it.

Common situations: Two `ScheduleLabel` types with similar names (e.g. a custom `Update`-like label vs `Update`); plugin ordering where removal runs before creation; cleanup code in tests assuming a schedule exists.

Related errors


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