nestjs/nest · critical · UnknownRequestMappingException
An invalid controller has been detected. "${className}" does
Error message
An invalid controller has been detected. "${className}" does not have the @Controller() decorator but it is being listed in the "controllers" array of some module. What it means
While mapping controllers to routes the framework reads the path metadata that the `@Controller()` decorator writes onto the class. A class listed in a module's `controllers` array without that decorator has no PATH_METADATA, so NestJS throws UnknownRequestMappingException — 'An invalid controller has been detected. X does not have the @Controller() decorator but it is being listed in the controllers array of some module.'
Source
Thrown at packages/core/router/router-explorer.ts:131
) {
const { instance } = instanceWrapper;
const routerPaths = this.pathsExplorer.scanForPaths(instance);
this.applyPathsToRouterProxy(
httpAdapterRef,
routerPaths,
instanceWrapper,
moduleKey,
routePathMetadata,
host,
options,
);
}
public extractRouterPath(metatype: Type<Controller>): string[] {
const path = Reflect.getMetadata(PATH_METADATA, metatype);
if (isUndefined(path)) {
throw new UnknownRequestMappingException(metatype);
}
if (Array.isArray(path)) {
return path.map(p => addLeadingSlash(p));
}
return [addLeadingSlash(path)];
}
public applyPathsToRouterProxy<T extends HttpServer>(
router: T,
routeDefinitions: RouteDefinition[],
instanceWrapper: InstanceWrapper,
moduleKey: string,
routePathMetadata: RoutePathMetadata,
host: string | RegExp | Array<string | RegExp>,
options: RouteResolutionOptions = {},
) {
(routeDefinitions || []).forEach(routeDefinition => {
const { version: methodVersion } = routeDefinition;View on GitHub (pinned to dd75d7bd8c)
Solutions
- Decorate the class: `@Controller('path')` on the exact class listed in the module.
- If the class is not meant to handle HTTP, move it to `providers` instead.
- Ensure the correct class is imported (check for default vs named import mismatches) and the decorator is applied to the class, not exported separately.
- Verify tsconfig: `experimentalDecorators: true` and, for emitted metadata, `emitDecoratorMetadata: true`.
Example fix
// before
export class CatsController { // decorator missing
@Get() findAll() { return []; }
}
// after
import { Controller, Get } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get() findAll() { return []; }
} Defensive patterns
Strategy: type-guard
Validate before calling
// Wiring test: every class in controllers arrays must carry controller metadata
import { CONTROLLER_WATERMARK, PATH_METADATA } from '@nestjs/common/constants';
import 'reflect-metadata';
const isNestController = (cls: any): boolean =>
typeof cls === 'function' &&
!!Reflect.getMetadata(CONTROLLER_WATERMARK, cls) &&
Reflect.getMetadata(PATH_METADATA, cls) !== undefined;
function assertControllersValid(module: Function) {
for (const c of Reflect.getMetadata('controllers', module) ?? []) {
if (!isNestController(c)) throw new Error(`${module.name}: ${c?.name} is not decorated with @Controller()`);
}
} Type guard
const isNestController = (cls: unknown): cls is import('@nestjs/common').Type<any> =>
typeof cls === 'function' && Reflect.getMetadata('__controller__', cls) !== undefined; Prevention
- Run the controllers-array metadata check above in a wiring unit test for every module.
- Keep controllers and providers in separate files so array mix-ups are caught by imports.
- Confirm experimentalDecorators/emitDecoratorMetadata stay enabled after build-tool migrations.
When it happens
Trigger: A plain class (service, DTO, utility) mistakenly added to a module's `controllers` array; the decorator was deleted or commented out during refactoring; the class is decorated by a custom pseudo-decorator that does not apply @Controller(); copy-paste of a controller file where the decorator line was not copied; decorators disabled in tsconfig (`experimentalDecorators: false`) so nothing gets written.
Common situations: Renaming a controller into a service but leaving it registered under controllers; large modules where arrays get long; upgrades to build tooling (swc/esbuild configs) that silently drop decorators when misconfigured; scaffolding mistakes.
Related errors
- "${metatype.name}" is decorated with @Controller() and canno
- Conflicting HTTP routes detected: - ${messages} Adjust rou
- Nest cannot create the ${parentModule.name} instance. The mo
- Nest cannot create the ${parentModule.name} instance. Receiv
- "${metatype.name}" is decorated with @Injectable() and canno
AI-assisted analysis of nestjs/nest@dd75d7bd8c (2026-08-21).
Data as JSON: /api/errors/6de80ed0853e0519.
Report an issue: GitHub.