halo-dev/halo · error · TypeError
PluginModule.routes must be an array.
Error message
PluginModule.routes must be an array.
What it means
Thrown by preparePluginModule when module.routes is present but not an array. routes must be RouteRecordRaw[] | RouteRecordAppend[] so Halo can iterate and register console routes (with meta/chunk wrapping). A non-array value would break the route registration loop, so the host throws a TypeError before touching the router.
Source
Thrown at ui/src/setup/setupModules.ts:671
componentOwners
);
}
}
function preparePluginModule(
module: PluginModule,
registration: UiProviderRegistration,
report: (
registration: UiProviderRegistration,
stage: UiPluginFailureStage,
error: unknown
) => void
) {
if (module.components && !isRecord(module.components)) {
throw new TypeError("PluginModule.components must be an object.");
}
if (module.routes && !Array.isArray(module.routes)) {
throw new TypeError("PluginModule.routes must be an array.");
}
if (module.ucRoutes && !Array.isArray(module.ucRoutes)) {
throw new TypeError("PluginModule.ucRoutes must be an array.");
}
if (module.formkit && !isRecord(module.formkit)) {
throw new TypeError("PluginModule.formkit must be an object.");
}
if (module.extensionPoints && !isRecord(module.extensionPoints)) {
throw new TypeError("PluginModule.extensionPoints must be an object.");
}
resetRouteMetaAndWrapChunks(module.routes, false, registration, report);
resetRouteMetaAndWrapChunks(module.ucRoutes, false, registration, report);
}
function resetRouteMetaAndWrapChunks(
routes: RouteRecordRaw[] | RouteRecordAppend[] | undefined,
core: boolean,
registration?: UiProviderRegistration,View on GitHub (pinned to d2f5165f9c)
Solutions
- Wrap the route(s) in an array: { routes: [{ path: '/x', component: X }] }.
- Omit routes if the plugin registers no console routes.
- Typecheck the plugin module against PluginModule before release.
Example fix
// before
export default definePluginModule({ routes: { name: "foo", path: "/foo", component: Foo } });
// after
export default definePluginModule({ routes: [{ name: "foo", path: "/foo", component: Foo }] }); Defensive patterns
Strategy: type-guard
Validate before calling
function assertRoutesShape(module: { routes?: unknown }) {
if (module.routes && !Array.isArray(module.routes)) {
throw new TypeError("PluginModule.routes must be an array.");
}
} Type guard
function isRouteArray(value: unknown): value is unknown[] {
return Array.isArray(value);
} Prevention
- Always wrap routes in an array literal, even for a single route.
- Typecheck the plugin module with vue-tsc.
- Add a spec that asserts Array.isArray(module.routes).
When it happens
Trigger: A plugin module returns { routes: { path: '/x' } } (a single object) or { routes: 'x' } during activation; preparePluginModule reads module.routes and Array.isArray fails.
Common situations: Plugin author returns one route object instead of an array; merges routes from a nested object; a typo assigns a string.
Related errors
- PluginModule.components must be an object.
- PluginModule.ucRoutes must be an array.
- PluginModule.formkit must be an object.
- Cannot restore a replaced route under an anonymous parent re
- Invalid attachment
AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14).
Data as JSON: /api/errors/7ddcc987e819aca1.
Report an issue: GitHub.