This article is published in English.
Diagnosing the NestJS Cannot Resolve Dependencies Error, Cause by Cause
Learn to read the NestJS dependency resolution error and fix its five usual causes: missing providers, unexported modules, cycles, wrong tokens and bare test modules.
Sooner or later every NestJS project stops at startup with a message saying the container cannot resolve a constructor argument. The wording looks opaque but is precise, and it nearly always traces back to one of five wiring mistakes. This guide shows how to read the message and walks through each cause in the order you should check them.
Reading the error message
A typical failure looks like this:
Nest can't resolve dependencies of the UsersService (?). Please make sure that the argument at index [0] is available in the UsersModule context.
The class named in the message (UsersService) is the one Nest was trying to instantiate. Inside the parentheses, each constructor parameter is listed, and the ? marks the one that failed; index [0] repeats that position. The final part names the module whose scope was searched. Every fix below is a way of making that argument visible.
Cause 1: the class was never registered as a provider
The simplest case: the service file exists, but no module lists it. Nest only manages classes that appear in a module's providers array.
@Module({
controllers: [UsersController],
providers: [UsersService], // <-- missing? that's your error
})
export class UsersModule {}
The CLI command nest generate service updates the module for you. Hand-written services are where this step gets forgotten.
Cause 2: the provider lives in another module that is not imported or not exported
This is the most frequent cross-module variant. AuthService is registered in AuthModule, and UsersService injects it, but UsersModule has no import pointing at AuthModule:
@Module({
imports: [AuthModule], // <-- without this, AuthService is invisible here
providers: [UsersService],
})
export class UsersModule {}
Importing is only half of the contract. The module that owns the provider must also list it under exports:
@Module({
providers: [AuthService],
exports: [AuthService], // <-- other modules can only use what you export
})
export class AuthModule {}
A useful mental model: a provider is private to its module unless exported, and an exported provider is still invisible to modules that do not import its owner. Both conditions must hold.
Cause 3: two services depend on each other
If UsersService needs OrdersService and OrdersService needs UsersService, neither can be built first. Nest offers forwardRef() to defer resolution. It is applied at the module level for the import:
// users.module.ts
@Module({
imports: [forwardRef(() => OrdersModule)],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}
and again at the injection site in the service constructor:
// users.service.ts
constructor(
@Inject(forwardRef(() => OrdersService))
private ordersService: OrdersService,
) {}
The mirror image is needed on the other side (OrdersModule importing forwardRef(() => UsersModule)). Consider this a temporary patch rather than a solution. A cycle usually means shared behaviour is living in the wrong place; moving it into a third module that both can import removes the cycle and the need for forwardRef entirely.
A related trap: circular file imports, often through barrel index.ts files, can make a class reference undefined at decoration time. Nest then reports an unresolvable dependency even though the modules look correct.
Cause 4: the injection token does not match
Custom providers are registered under a token, and injection must use exactly that token. Consider a value provider keyed by a string:
{
provide: 'CONFIG_OPTIONS',
useValue: configOptions,
}
Declaring the constructor parameter with only a type will not find it, because the type is not the token. Use @Inject() with the same string:
constructor(@Inject('CONFIG_OPTIONS') private config: ConfigOptions) {}
Types in TypeScript also disappear at runtime, which is why an interface can never serve as a token on its own.
TypeORM repositories fail the same way. Typing the parameter as a repository class does not match the token Nest registered:
// Wrong
constructor(private repo: UserRepository) {}
The working form uses @InjectRepository() with the entity:
// Right
constructor(
@InjectRepository(User)
private repo: Repository<User>,
) {}
For that token to exist in the first place, the module must also import TypeOrmModule.forFeature([User]).
Cause 5: the testing module lacks providers or mocks
Sometimes the application starts cleanly while unit tests throw the identical error. Test.createTestingModule creates a brand-new container containing only what you declare, so every dependency of the class under test must be provided, typically as a mock bound to the correct token:
const module = await Test.createTestingModule({
providers: [
UsersService,
{
provide: getRepositoryToken(User),
useValue: mockRepository, // <-- every dependency needs one of these
},
],
}).compile();
getRepositoryToken(User) produces the same token that @InjectRepository(User) looks for. If the error only appears in tests, the production wiring is fine and the test setup is incomplete.
A debugging checklist
Work through these in order:
- Is the class listed in the relevant module's
providers? - Is the owning module imported where the provider is used, and does it export the provider?
- Is there a circular dependency, either between services or between files? Patch with
forwardRef, then refactor. - For custom providers and repositories, does the injection token match the registration exactly?
- Does the failure occur only in tests? Add the missing providers or mocks.
Key takeaways
- The
?and the index in the message identify exactly which constructor argument is missing, and in which module's scope. - Visibility in Nest is explicit: register, export, import.
forwardRefhides cycles; extracting a shared module fixes them.- Tokens, not TypeScript types, drive injection at runtime.
- Test modules are separate containers and need their own complete wiring.
For more on keeping module boundaries healthy as a codebase grows, see six DDD rules for structuring domains in NestJS apps.