bevyengine/bevy · error

Component already has an on_insert hook

Error message

Component already has an on_insert hook

What it means

Panic in ComponentLifecycleHook registration (on_insert): the try_on_insert call found an existing on_insert hook in the component's lifecycle slot. The expect guard prevents a second registration from overwriting the first, which would hide a real bug in hook wiring.

Source

Thrown at crates/bevy_ecs/src/lifecycle.rs:207

            .expect("Component already has an on_add hook")
    }

    /// Register a [`ComponentHook`] that will be run when this component is added (with `.insert`)
    /// or replaced.
    ///
    /// An `on_insert` hook always runs after any `on_add` hooks (if the entity didn't already have the component).
    ///
    /// # Warning
    ///
    /// The hook won't run if the component is already present and is only mutated, such as in a system via a query.
    /// As a result, this needs to be combined with immutable components to serve as a mechanism for reliably updating indexes and other caches.
    ///
    /// # Panics
    ///
    /// Will panic if the component already has an `on_insert` hook
    pub fn on_insert(&mut self, hook: ComponentHook) -> &mut Self {
        self.try_on_insert(hook)
            .expect("Component already has an on_insert hook")
    }

    /// Register a [`ComponentHook`] that will be run when this component is about to be dropped,
    /// such as being replaced (with `.insert`) or removed.
    ///
    /// If this component is inserted onto an entity that already has it, this hook will run before the value is replaced,
    /// allowing access to the previous data just before it is dropped.
    /// This hook does *not* run if the entity did not already have this component.
    ///
    /// An `on_discard` hook always runs before any `on_remove` hooks (if the component is being removed from the entity).
    ///
    /// # Warning
    ///
    /// The hook won't run if the component is already present and is only mutated, such as in a system via a query.
    /// As a result, this needs to be combined with immutable components to serve as a mechanism for reliably updating indexes and other caches.
    ///
    /// # Panics
    ///

View on GitHub (pinned to 396ca72708)

Solutions

  1. Merge the new behavior into the already-registered on_insert hook closure
  2. Audit plugins/extension traits for duplicate .on_insert(...) calls on the same component
  3. Use try_on_insert and handle the None case explicitly when conflict is expected
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at crates/bevy_ecs/src/lifecycle.rs:207 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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