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

  1. Rename the custom route path so it does not start with the prefix (e.g. '/custom/api/agents' or '/my-api/...')
  2. If you truly intend to override a built-in route, use the documented built-in-route override mechanism instead of a custom route
  3. 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

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


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/7caf1710c2efabb3. Report an issue: GitHub.