bevyengine/bevy · error · GetEntityMutByIdError

the `Component` is immutable

Error message

the `Component` is immutable

What it means

GetEntityMutByIdError::ComponentIsImmutable is returned by UnsafeEntityCell::get_mut_by_id when the target component was declared immutable via #[component(immutable)]. Immutable components forbid &mut access by design (their value is fixed after insertion), so requesting a mutable reference violates their invariants and the API refuses instead of handing out an aliasing pointer.

Source

Thrown at crates/bevy_ecs/src/world/unsafe_world_cell.rs:1226

        // SAFETY: UnsafeEntityCell is only constructed for living entities and offers no despawn method
        unsafe {
            self.world()
                .entities()
                .entity_get_spawned_or_despawned_unchecked(self.entity)
                .1
        }
    }
}

/// Error that may be returned when calling [`UnsafeEntityCell::get_mut_by_id`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
pub enum GetEntityMutByIdError {
    /// The [`ComponentInfo`](crate::component::ComponentInfo) could not be found.
    #[error("the `ComponentInfo` could not be found")]
    InfoNotFound,
    /// The [`Component`] is immutable. Creating a mutable reference violates its
    /// invariants.
    #[error("the `Component` is immutable")]
    ComponentIsImmutable,
    /// This [`Entity`] does not have the desired [`Component`].
    #[error("the `Component` could not be found")]
    ComponentNotFound,
}

impl<'w> UnsafeWorldCell<'w> {
    #[inline]
    /// # Safety
    /// - the returned `Table` is only used in ways that this [`UnsafeWorldCell`] has permission for.
    /// - the returned `Table` is only used in ways that would not conflict with any existing borrows of world data.
    unsafe fn fetch_table(self, location: EntityLocation) -> Option<&'w Table> {
        // SAFETY:
        // - caller ensures returned data is not misused and we have not created any borrows of component/resource data
        // - `location` contains a valid `TableId`, so getting the table won't fail
        unsafe { self.storages().tables.get(location.table_id) }
    }

View on GitHub (pinned to 396ca72708)

Solutions

  1. Use shared access instead: get_by_id(component_id) returns the read-only pointer.
  2. To 'change' an immutable component, despawn or use an entity-level API that replaces it (e.g. insert overwrites are still handled by the typed path — check the component's contract; mutation via &mut is not allowed).
  3. In generic code, branch on world.components().get_info(id).map(|i| i.is_immutable()) before choosing get_mut_by_id vs get_by_id.
  4. Reconsider whether the component really should be immutable if your design requires mutation.

Example fix

// before
let c = cell.get_mut_by_id(id).unwrap(); // ComponentIsImmutable

// after
if cell.world().components().get_info(id).is_some_and(|i| i.is_immutable()) {
    let c = cell.get_by_id(id); // shared access
} else {
    let c = cell.get_mut_by_id(id);
}
Defensive patterns

Strategy: type-guard

Validate before calling

let immutable = world
    .components()
    .get_info(component_id)
    .is_some_and(|info| info.is_immutable());

Type guard

fn is_immutable(world: &World, id: ComponentId) -> bool {
    world
        .components()
        .get_info(id)
        .map(|info| info.is_immutable())
        .unwrap_or(false)
}

Try / catch

match cell.get_mut_by_id(component_id) {
    Err(GetEntityMutByIdError::ComponentIsImmutable) => {
        let shared = cell.get_by_id(component_id); // fall back to read-only access
    }
    other => { /* ... */ }
}

Prevention

When it happens

Trigger: Calling get_mut_by_id(component_id) for any component annotated #[component(immutable)]; generic/dynamic code that uniformly fetches components mutably by id without knowing which ones are immutable; tools (editors, dev-tools) that iterate all components of an entity and request &mut for each.

Common situations: Marking relationship hooks / fixed metadata components immutable and then hitting them from a generic mutator; refactoring a component to immutable without updating the code that mutates it; unsafe schedule code that assumes every component is mutable.

Related errors


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