bevyengine/bevy · critical

Encountered a mismatched World. This SystemState was created

Error message

Encountered a mismatched World. This SystemState was created from {this:?}, but a method was called using {other:?}.

What it means

SystemState caches a system's parameter state and is bound to the WorldId of the World it was built from. validate_world panics when a SystemState method is invoked against a World with a different id, because the cached state would be invalid for that world. This is bevy's guard against silently reusing per-world caches across worlds.

Source

Thrown at crates/bevy_ecs/src/system/function_system.rs:423

        Param::apply(&mut self.param_state, &self.meta, world);
    }

    /// Returns `true` if `world_id` matches the [`World`] that was used to call [`SystemState::new`].
    /// Otherwise, this returns false.
    #[inline]
    pub fn matches_world(&self, world_id: WorldId) -> bool {
        self.world_id == world_id
    }

    /// Asserts that the [`SystemState`] matches the provided world.
    #[inline]
    #[track_caller]
    fn validate_world(&self, world_id: WorldId) {
        #[inline(never)]
        #[track_caller]
        #[cold]
        fn panic_mismatched(this: WorldId, other: WorldId) -> ! {
            panic!("Encountered a mismatched World. This SystemState was created from {this:?}, but a method was called using {other:?}.");
        }

        if !self.matches_world(world_id) {
            panic_mismatched(self.world_id, world_id);
        }
    }

    /// Retrieve the [`SystemParam`] values.
    ///
    /// Returns an error if system parameter validation fails.
    ///
    /// # Safety
    /// This call might access any of the input parameters in a way that violates Rust's mutability rules. Make sure the data
    /// access is safe in the context of global [`World`] access. The passed-in [`World`] _must_ be the [`World`] the [`SystemState`] was
    /// created with.
    #[inline]
    #[track_caller]
    pub unsafe fn get_unchecked<'w, 's>(

View on GitHub (pinned to 396ca72708)

Solutions

  1. Store the WorldId next to the state and rebuild whenever SystemState::matches_world(world.id()) is false.
  2. Do not cache SystemState in statics/globals across App instances; key it per world or construct it per call site.
  3. In tests, create a fresh SystemState per test/world.

Example fix

// before
static mut STATE: Option<SystemState<Query<&Transform>>> = None; // reused across Worlds

// after
struct Cached {
    world_id: WorldId,
    state: SystemState<Query<'static, 'static, &'static Transform>>,
}
fn cached(world: &mut World, slot: &mut Option<Cached>) -> &mut Cached {
    let id = world.id();
    if !slot.as_ref().is_some_and(|c| c.world_id == id) {
        *slot = Some(Cached { world_id: id, state: SystemState::new(world) });
    }
    slot.as_mut().unwrap()
}
Defensive patterns

Strategy: type-guard

Validate before calling

if !state.matches_world(world.id()) {
    *state = SystemState::new(world); // rebuild the cache for this world
}
// safe to call state.get_mut(world)

Type guard

fn state_matches_world<S: SystemParam>(state: &SystemState<S>, world: &World) -> bool {
    state.matches_world(world.id())
}

Prevention

When it happens

Trigger: Creating SystemState<Q> with one World and calling .get()/.get_mut() with another: static or cached state shared across two App/World instances, state extracted in a helper and reused after the world was rebuilt, or multi-world setups (editor preview world plus main world) sharing one state.

Common situations: Static/cached SystemState reused across tests that each create their own App; performance-motivated state caches that outlive their world; refactors that move world-creating code while keeping the cache.

Related errors


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