mastra-ai/mastra · error · Error
Custom API route "${route.path}" must not start with "${pref
Error message
Custom API route "${route.path}" must not start with "${prefix}" — that path is reserved for built-in Mastra routes. Choose a different path (e.g. "${route.path.replace(prefix, '/custom')}"). What it means
Custom user-defined API routes may not occupy the same path prefix as the built-in Mastra server routes (the configured prefix, default '/api'). This check at server startup prevents custom handlers from shadowing or conflicting with internal routes. It throws with the offending path and a suggested alternative.
Source
Thrown at packages/server/src/server/server-adapter/index.ts:1018
const schemaRoutes = routes.filter(isSchemaApiRoute);
for (const route of schemaRoutes) {
await this.registerRoute(this.app, route as unknown as ServerRoute, { prefix: '' });
}
return routes.filter((route): route is HonoCustomApiRoute => !isSchemaApiRoute(route));
}
/**
* Validates that no custom route path collides with the built-in route prefix.
* Throws if any route path starts with the server's `apiPrefix`.
*/
protected validateCustomRoutePaths(routes: ApiRoute[]): void {
const prefix = this.prefix ?? '';
if (!prefix) return;
for (const route of routes) {
if (route._mastraInternal) continue;
if (route.path.startsWith(`${prefix}/`) || route.path === prefix) {
throw new Error(
`Custom API route "${route.path}" must not start with "${prefix}" — ` +
`that path is reserved for built-in Mastra routes. ` +
`Choose a different path (e.g. "${route.path.replace(prefix, '/custom')}").`,
);
}
}
}
/**
* Creates an internal Hono sub-app with all custom API routes registered.
* Stores the handler on this instance for use by handleCustomRouteRequest().
* Returns true if custom routes were found and registered.
*/
protected async buildCustomRouteHandler(routes: HonoCustomApiRoute[]): Promise<boolean> {
if (routes.length === 0) return false;
const NOT_FOUND_HEADER = 'x-mastra-custom-route-not-found';
const mastra = this.mastra;View on GitHub (pinned to 75dd419e61)
Solutions
- Rename the custom route path so it does not start with the prefix (e.g. '/custom/api/agents' or '/my-api/...')
- If you truly intend to override a built-in route, use the documented built-in-route override mechanism instead of a custom route
- Check the server 'prefix' option — a changed prefix may now include routes that previously did not collide
Example fix
// before
registerApiRoute('/api/agents/custom', { method: 'POST', handler })
// after
registerApiRoute('/custom/agents/custom', { method: 'POST', handler }) Defensive patterns
Strategy: validation
Validate before calling
const prefix = '/api';
const bad = routes.filter(r => !r._mastraInternal && (r.path === prefix || r.path.startsWith(prefix + '/')));
if (bad.length) throw new Error('Reserved prefix collision: ' + bad.map(r => r.path)); Type guard
const collidesWithPrefix = (p: string, prefix: string): boolean =>
p === prefix || p.startsWith(`${prefix}/`); Prevention
- Prefix custom routes with a non-reserved namespace
- Test server boot in CI to catch collisions at deploy time
- Document the reserved prefix for the team
When it happens
Trigger: Calling registerApiRoute (or passing routes to the server config) with a path equal to or nested under the server prefix, e.g. '/api/agents' when prefix is '/api', unless the route is marked _mastraInternal.
Common situations: Defining a custom proxy for built-in endpoints and accidentally reusing '/api/...'; changing the server 'prefix' config so existing custom routes now collide; copy-pasting built-in route paths into custom route definitions.
Related errors
- [mastra/auth-ee] FGA is configured but ${missingRoutes.lengt
- AcpAgent does not support resuming suspended generate calls
- ACP prompt stopped before completing: ${response.stopReason}
- Auth0 domain and audience are required, please provide them
- Cookie password must be at least 32 characters for SSO. Set
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/7caf1710c2efabb3.
Report an issue: GitHub.