halo-dev/halo · critical · Error
Cannot restore a replaced route under an anonymous parent re
Error message
Cannot restore a replaced route under an anonymous parent registered outside Halo's managed route setup.
What it means
Thrown by restoreReplacedRoute when a plugin that previously replaced a named route is being torn down/rolled back, but that replaced route was nested under an anonymous parent route (a parent with no name). Restoring requires re-adding the route under its parent by parentName; an anonymous parent has no name to target, so Halo cannot safely re-insert the route and aborts to avoid a corrupted route table. This is a host-side invariant: route replacement is only restorable when the parent route is named within Halo's managed setup.
Source
Thrown at ui/src/setup/setupModules.ts:639
routes: ReturnType<Router["getRoutes"]>,
name: string | symbol
) {
return routes.find((route) =>
route.children?.some((child) => child.name === name)
);
}
function restoreReplacedRoute(
router: Router,
replacedRoute: ReplacedRoute | undefined,
routeOwners?: Map<string | symbol, UiProviderRegistration>,
componentOwners?: WeakMap<object, UiProviderRegistration>
) {
if (!replacedRoute) {
return;
}
if (replacedRoute.nestedUnderAnonymousParent) {
throw new Error(
"Cannot restore a replaced route under an anonymous parent registered outside Halo's managed route setup."
);
}
if (replacedRoute.parentName) {
router.addRoute(replacedRoute.parentName, replacedRoute.route);
} else {
router.addRoute(replacedRoute.route);
}
if (replacedRoute.owner) {
registerRouteOwners(
replacedRoute.route,
replacedRoute.owner,
routeOwners,
componentOwners
);
}
}
View on GitHub (pinned to d2f5165f9c)
Solutions
- Avoid using route replacement (parentName append targeting a replace) for routes whose parent is not a Halo-named managed route; register a new named route instead.
- Ensure any parent route involved in a replace is registered with an explicit name in Halo's managed setup, not an anonymous route.
- Update @halo-dev components/console to a version where the relevant parent routes carry stable names.
Example fix
// before (plugin module)
export default definePluginModule({
routes: [{ parentName: "<unnamed-layout>", route: { name: "myPage", path: "page", component: Page } }]
});
// after
export default definePluginModule({
routes: [{ name: "myPage", path: "my-plugin/page", component: Page }]
}); Defensive patterns
Strategy: validation
Validate before calling
// Plugin author guard: do not replace routes under unnamed parents.
import type { Router } from "vue-router";
function assertReplaceSafe(router: Router, parentName: string | symbol) {
const parent = router.getRoutes().find((r) => r.name === parentName);
if (!parent || !parent.name) {
throw new Error(`Cannot replace under anonymous/unnamed parent ${String(parentName)}. Register a new named route instead.`);
}
} Type guard
function isNamedParent(route: { name?: unknown }): boolean {
return route.name !== undefined && route.name !== null &&
(typeof route.name === "string" || typeof route.name === "symbol");
} Prevention
- Register your own named top-level route rather than replacing one under a layout wrapper.
- Avoid route replacement (parentName append) in plugins; prefer standalone named routes.
- Keep the Halo console and plugin SDK versions aligned so managed parent routes stay named.
When it happens
Trigger: During plugin deactivation/uninstall rollback (the call site around line 523), restoreReplacedRoute receives a ReplacedRoute whose nestedUnderAnonymousParent is true. That flag was set in findReplacedRoute as Boolean(parent && !parent.name) — the existing route's parent in router.getRoutes() lacks a name.
Common situations: A plugin replaces a route whose parent is a wrapper/layout route registered without a name (anonymous Symbol-named parent created by assignInternalRouteParentNames only covers Halo-managed routes); a third-party router modification introduces an unnamed parent above a replaceable route; conflicting plugins that restructure the route tree before rollback.
Related errors
- PluginModule.components must be an object.
- PluginModule.routes must be an array.
- PluginModule.formkit must be an object.
- PluginModule.ucRoutes must be an array.
AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14).
Data as JSON: /api/errors/6b7bc38b2f270b98.
Report an issue: GitHub.