Description
Vue version
3.4 x
Link to minimal reproduction
Steps to reproduce
Create a Vue 3.4 project (using Vite, Vue CLI, or similar)
Install and configure vue-router@4
Define the following routes:
{
path: "/teacher/class/:id",
name: "teacher class",
component: ClassOverview,
},
{
path: "/teacher/class/:id/badges",
name: "teacher class badges",
component: ClassBadgeList,
},
{
path: "/teacher/class/:id/badges/:badgeId",
name: "teacher class badge",
component: ClassBadgeDetail,
},
Use a standard App.vue file with a dynamic key on :
<template>
<router-view :key="$route.fullPath" />
</template>
Use a minimal component for ClassBadgeList.vue:
<template>
<div>
<p>BADGES PAGE</p>
</div>
</template>
Start the dev server and navigate in the browser to:
/teacher/class/123 ✅ — works
/teacher/class/123/badges/456 ✅ — works
/teacher/class/123/badges ❌ — crashes with DOM error
What is expected?
Navigating to /teacher/class/:id/badges
should correctly render the ClassBadgeList.vue
component like any other route. The page should mount cleanly without any DOM or runtime errors, as happens with the sibling routes.
What is actually happening?
When navigating to /teacher/class/:id/badges
, Vue throws a fatal runtime error in the browser:
Uncaught (in promise) NotFoundError: Failed to execute 'insertBefore' on 'Node':
The node before which the new node is to be inserted is not a child of this node.
This occurs inside Vue’s internal patching logic (processFragment or patch) and causes the app to crash.
All other routes — including /teacher/class/:id
and /teacher/class/:id/badges/:badgeId
— work without issue.
System Info
System:
OS: macOS Ventura 13.5
CPU: Apple M1 Pro
Binaries:
Node: 18.x
npm: 9.x
Yarn: 1.22.x
Browsers:
Chrome: 125
Firefox: 127
Safari: 17.x
npmPackages:
vue: ^3.4.x
vue-router: ^4.2.x
Any additional comments?
This error only affects the /teacher/class/:id/badges route
. All other routes, including similarly structured dynamic routes (e.g. /teacher/class/:id, /teacher/class/:id/badges/:badgeId
) work as expected.
I’ve already tried all of the following:
- Reordering routes (moving /badges above :id or below)
- Renaming the route path (e.g., /class-badges)
- Simplifying the component to a static
BADGES PAGE
- Ensuring clean index.html and #app mount point
- Using :key="$route.fullPath" on
- Removing all transitions, fragments, suspense, and conditional logic
- Using components: { default: ClassBadgeList } vs component: ClassBadgeList
- Verifying other dynamic pages render fine
All attempts failed only for this specific path structure — leading me to believe this is a low-level vnode or fragment anchor regression introduced in Vue 3.4.
This pattern has worked without issue in many other Vue 3 projects I've built.