nestjs/nest · critical · UndefinedDependencyException
Nest can't resolve dependencies of the ${type.toString()}
Error message
Nest can't resolve dependencies of the ${type.toString()} What it means
Thrown while the injector resolves constructor parameters: the resolved dependency token is `undefined`. NestJS reads constructor types from the `design:paramtypes` metadata that TypeScript emits (requires `emitDecoratorMetadata`); when that metadata contains `undefined` the class cannot be instantiated, so the container fails fast with UndefinedDependencyException instead of passing `undefined` into your constructor. In nearly all cases the `undefined` comes from the ES module system, not from your module configuration.
Source
Thrown at packages/core/injector/injector.ts:519
}
public reflectSelfParams(type: Type<unknown> | Function): any[] {
return Reflect.getMetadata(SELF_DECLARED_DEPS_METADATA, type) || [];
}
public async resolveSingleParam<T>(
wrapper: InstanceWrapper<T>,
param: Type<any> | string | symbol,
dependencyContext: InjectorDependencyContext,
moduleRef: Module,
resolutionContext: ResolutionContext = { contextId: STATIC_CONTEXT },
keyOrIndex?: symbol | string | number,
) {
if (isUndefined(param)) {
this.logger.log(
'Nest encountered an undefined dependency. This may be due to a circular import or a missing dependency declaration.',
);
throw new UndefinedDependencyException(
wrapper.name,
dependencyContext,
moduleRef,
);
}
const token = this.resolveParamToken(wrapper, param);
return this.resolveComponentWrapper(
moduleRef,
token,
dependencyContext,
wrapper,
resolutionContext,
keyOrIndex,
);
}
public resolveParamToken<T>(
wrapper: InstanceWrapper<T>,View on GitHub (pinned to 39fbddae51)
Solutions
- Break the file-level circular import: move the shared types/interfaces to a third file that neither side depends on, or import one side directly instead of via a barrel.
- If the classes genuinely need each other, use `@Inject(forwardRef(() => OtherService)) private readonly other: OtherService` on the injection side (and `moduleRef`/`forwardRef` in modules if needed).
- Resolve the dependency lazily at runtime with `this.moduleRef.get(OtherService)` or `moduleRef.resolve()` inside a method instead of the constructor.
- Confirm `"emitDecoratorMetadata": true` and `"experimentalDecorators": true` in tsconfig.json, especially after switching to esbuild/swc based builds that drop metadata by default.
- Make sure the injected value is a concrete class or a real token, never an interface without `@Inject(token)`.
Example fix
// before (files import each other -> design:paramtypes captures undefined)
// a.service.ts
@Injectable()
export class AService {
constructor(private readonly b: BService) {} // BService is undefined here
}
// after
import { Inject, forwardRef } from '@nestjs/common';
@Injectable()
export class AService {
constructor(
@Inject(forwardRef(() => BService))
private readonly b: BService,
) {}
} Defensive patterns
Strategy: validation
Validate before calling
// Pre-bootstrap scan: no provider constructor may reference undefined tokens
import 'reflect-metadata';
function assertNoUndefinedParams(classes: Function[]) {
for (const cls of classes) {
const params: any[] = Reflect.getOwnMetadata('design:paramtypes', cls) ?? [];
params.forEach((t, i) => {
if (t === undefined) {
throw new Error(
`${cls.name}: constructor param #${i} resolved to undefined — circular import or non-emitted type`,
);
}
});
}
}
// run before NestFactory.create(AppModule) with all provider classes Type guard
const hasDefinedParamTypes = (cls: Function): cls is new (...args: any[]) => any =>
(Reflect.getOwnMetadata('design:paramtypes', cls) ?? []).every(t => t !== undefined); Try / catch
try {
await NestFactory.create(AppModule);
} catch (e: any) {
if (/can't resolve dependencies/i.test(String(e?.message))) {
console.error('DI wiring failure at bootstrap:', e.message);
process.exit(1);
}
throw e;
} Prevention
- Keep "emitDecoratorMetadata": true and verify transpiler configs (swc/esbuild) that drop metadata silently.
- Ban barrel-file re-exports of injectable classes or audit cycles with eslint-plugin-import (import/no-cycle).
- Use @Inject(forwardRef(() => X)) at the first sign of a two-way dependency instead of waiting for runtime failure.
- Never inject interfaces without an explicit @Inject(token); interfaces emit Object/undefined metadata.
When it happens
Trigger: resolveSingleParam is called with `param === undefined`: (1) two provider files import each other, so at decoration time the imported class binding is still `undefined`; (2) a provider is imported through a barrel file (index.ts) that participates in an import cycle; (3) `@Inject(TOKEN)` where the TOKEN constant is `undefined` when the decorator evaluates (again due to a cycle); (4) the parameter type is imported from a file whose exports are not yet initialized.
Common situations: Two services in different files reference each other's types; extracting classes into index.ts barrels during a refactor; introducing TypeScript path aliases that create cycles; importing a token constant from a file that also imports the consumer. Developers often see this right after adding a new cross-service dependency.
Related errors
- Nest cannot create the ${parentModule.name} instance. The mo
- ModuleRef cannot instantiate class (${value} is not construc
- Nest could not find ${name} element (this provider does not
- The middleware doesn't provide the 'use' method (${name})
- Calling the "${methodName}" in the preview mode is not suppo
AI-assisted analysis of nestjs/nest@39fbddae51 (2026-08-21).
Data as JSON: /api/errors/8960482df8897617.
Report an issue: GitHub.