ionic-team/ionic-framework · error · Error

inserted view was already destroyed

Error message

inserted view was already destroyed

What it means

Thrown in IonNav.prepareTI() when an inserted view's `state` is `VIEW_STATE_DESTROYED`. Once a view has been destroyed (removed and torn down by the nav), it cannot be re-inserted because its DOM and lifecycle resources are gone. The nav refuses to revive a destroyed controller.

Source

Thrown at core/src/components/nav/nav.tsx:742

    if (!insertViews) {
      return;
    }
    assert(insertViews.length > 0, 'length can not be zero');
    const viewControllers = convertToViews(insertViews);

    if (viewControllers.length === 0) {
      throw new Error('invalid views to insert');
    }

    // Check all the inserted view are correct
    for (const view of viewControllers) {
      view.delegate = ti.opts.delegate;
      const nav = view.nav;
      if (nav && nav !== this) {
        throw new Error('inserted view was already inserted');
      }
      if (view.state === VIEW_STATE_DESTROYED) {
        throw new Error('inserted view was already destroyed');
      }
    }
    ti.insertViews = viewControllers;
  }

  /**
   * Returns the view that will be entered considering the transition instructions.
   *
   * @param ti The instructions.
   * @param leavingView The view being left or undefined if none.
   *
   * @returns The view that will be entered, undefined if none.
   */
  private getEnteringView(
    ti: TransitionInstruction,
    leavingView: ViewController | undefined
  ): ViewController | undefined {
    // The last inserted view will be entered when view are inserted.

View on GitHub (pinned to 625f9c38ad)

Solutions

  1. Never re-insert a destroyed ViewController; always insert the component class/element and let the nav build a new view.
  2. Discard cached controller references once the view has been popped.
  3. If you need stateful reuse, store the component props and re-create the view via setRoot/push with those props.
  4. Audit for any code path that retains `getActive()`/`getByIndex()` results across pop operations.

Example fix

// before
const view = nav.getActive();
await nav.pop();
await nav.push(view); // view is destroyed -> throws
// after
const { component, componentProps } = view;
await nav.pop();
await nav.push(component, componentProps);
Defensive patterns

Strategy: validation

Validate before calling

// Do not re-insert destroyed views; check state if you must reuse a reference
import { VIEW_STATE_DESTROYED } from '@ionic/core'; // conceptual
if (viewController.state === VIEW_STATE_DESTROYED) {
  throw new Error('Cannot re-insert a destroyed view. Push its component instead.');
}
await nav.push(viewController.component, viewController.componentProps);

Type guard

function isLiveView(view: ViewController): boolean {
  return view && view.state !== VIEW_STATE_DESTROYED;
}

Prevention

When it happens

Trigger: Passing a ViewController that was previously popped (and thus destroyed) back into `insert`/`push`/`setPages`/`setRoot`; caching a view reference and reusing it after the nav destroyed it.

Common situations: Holding a reference from `nav.getActive()` or `nav.getByIndex()`, popping the view, then trying to push the same controller again; pooling/caching views for performance and reusing them after destruction.

Related errors


AI-assisted analysis of ionic-team/ionic-framework@625f9c38ad (2026-08-12). Data as JSON: /api/errors/67139541c1737d96. Report an issue: GitHub.