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
- Use shared access instead: get_by_id(component_id) returns the read-only pointer.
- 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).
- 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.
- 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
- In generic per-component tooling, branch on ComponentInfo::is_immutable before choosing get_mut_by_id.
- Read the component's declaration: #[component(immutable)] forbids &mut by contract.
- Prefer shared access for inspection tools (editors, dev-tools).
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
- the `Component` could not be found
- The given `Entity` {entity} does not have a `{component_name
- the `ComponentInfo` could not be found
- Union types are not supported yet.
- Expected a Template type path
AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20).
Data as JSON: /api/errors/77addc90fe981df7.
Report an issue: GitHub.