ionic-team/ionic-framework · error · Error

inserted view was already inserted

Error message

inserted view was already inserted

What it means

Thrown in IonNav.prepareTI() while validating each inserted view: a view being inserted already has `view.nav` set to a different nav instance. Views track which nav owns them; inserting a view that another nav already claims would corrupt both stacks, so the nav rejects it.

Source

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

    }

    const insertViews = ti.insertViews;
    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,

View on GitHub (pinned to 625f9c38ad)

Solutions

  1. Do not pass live ViewController instances between navs; pass the underlying component and let the target nav create a fresh view.
  2. If you genuinely need to move a view, pop it from the source nav first and then insert the component (not the controller) into the target.
  3. Use `nav.getByIndex`/`getActive` only to read state, never to relocate the controller into another nav.
  4. Audit code that calls `insert`/`insertPages` for any controller obtained from `getViews()`/`getActive()`.

Example fix

// before
const view = otherNav.getActive();
await thisNav.insert(0, view);
// after
const { component, componentProps } = view;
await otherNav.removeIndex(otherNav.getViews().indexOf(view), 1);
await thisNav.insert(0, component, componentProps);
Defensive patterns

Strategy: validation

Validate before calling

// Never pass a live controller to another nav; insert the component instead
if (viewController.nav && viewController.nav !== nav) {
  throw new Error('View already belongs to another nav. Insert its component instead.');
}
await nav.insert(0, viewController.component, viewController.componentProps);

Type guard

function isOwnedBy(view: ViewController, nav: HTMLIonNavElement): boolean {
  return !view.nav || view.nav === nav;
}

Prevention

When it happens

Trigger: Passing a ViewController to `insert`/`push`/`setPages` that was obtained from another IonNav (e.g. via `nav.getActive()` on a sibling) without detaching it first; reusing a live ViewController instance across two navs.

Common situations: Sharing a view reference between a parent and child IonNav; manually constructing/obtaining a ViewController and pushing it into a second nav; advanced nested-nav setups that move views between stacks.

Related errors


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