bevyengine/bevy · error

Component already has an on_add hook

Error message

Component already has an on_add hook

What it means

Panic in ComponentLifecycleHook registration (on_add): try_on_add returned None because the component's lifecycle storage already holds an on_add hook. Registering a second hook for the same event would silently drop one, so the builder method uses expect as a guard against double registration.

Source

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

            self.on_remove(hook);
        }
        if let Some(hook) = C::on_despawn() {
            self.on_despawn(hook);
        }

        self
    }

    /// Register a [`ComponentHook`] that will be run when this component is added to an entity.
    /// An `on_add` hook will always run before `on_insert` hooks. Spawning an entity counts as
    /// adding all of its components.
    ///
    /// # Panics
    ///
    /// Will panic if the component already has an `on_add` hook
    pub fn on_add(&mut self, hook: ComponentHook) -> &mut Self {
        self.try_on_add(hook)
            .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")

View on GitHub (pinned to 396ca72708)

Solutions

  1. Register each hook kind once per component; consolidate logic into the existing hook closure
  2. Remove a prior .on_add(...) call or plugin that already registered the hook
  3. Use try_on_add to handle pre-existing hooks gracefully instead of panicking
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at crates/bevy_ecs/src/lifecycle.rs:189 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/fed5db95d1268a30. Report an issue: GitHub.