Skip to main content

Cloudsoft Zone Build, deploy and scale with practical guides to PHP, Node.js, React, DevOps and AI automation. Explore the guides

Cloudsoft Zone
DevelopmentJavaScriptNode.jsTroubleshooting guide

Fix "Nest can't resolve dependencies": every cause and its fix

The NestJS dependency error names a provider, an index and a module. Read it, then fix missing exports, interface tokens, circular imports and ESM traps.

Cloudsoft Zone

Updated 7 min read

Partly assembled jigsaw puzzle on a cardboard sheet with a box of loose pieces beside it
Photograph: Balise42 / CC BY-SA 4.0, via Wikimedia Commons
In this article

"Nest can't resolve dependencies of the UsersService (?)" means the Nest injector tried to build UsersService and could not find a provider for one constructor argument in the module where UsersService is registered. The message tells you which argument (the index), which token it looked for, and in which module it looked. Almost every case is one of six causes, and each has a specific fix.

The examples below were run against NestJS 12.1 on Node.js 24, and the causes follow the NestJS "Common errors" FAQ.

Read the error message first

The standard form is:

Text
Nest can't resolve dependencies of the UsersService (?).
Please make sure that the argument Symbol(MAILER) at index [0]
is available in the UsersModule module.
  • UsersService (?): the class being built. Each constructor argument appears in the brackets, with ? marking the one Nest could not resolve.
  • Symbol(MAILER): the injection token it looked for. If this is missing from the message, the token was undefined at runtime, which points to a different set of causes (see below).
  • at index [0]: the position of the argument in the constructor.
  • in the UsersModule module: where Nest searched. The provider must be registered in this module or exported by a module this one imports.

For more detail, set the NEST_DEBUG environment variable to any non-empty string. Nest then logs each resolution step, colour-coding the host class, the dependency token and the module it searched.

Shell
NEST_DEBUG=1 npm run start:dev

Cause 1: the provider is not visible in the module

This is the most common case. A provider is only visible inside the module that declares it, unless that module exports it and the consuming module imports that module. Here MailModule provides MAILER, but UsersModule forgot to import it:

TypeScript
import { Inject, Injectable, Module } from '@nestjs/common';

export const MAILER = Symbol('MAILER');
export interface Mailer { send(to: string): Promise<void> }

@Injectable()
export class UsersService {
  constructor(@Inject(MAILER) private readonly mailer: Mailer) {}
}

@Module({
  providers: [{ provide: MAILER, useValue: { send: async () => {} } }],
  exports: [MAILER],            // 1. export the token
})
export class MailModule {}

@Module({
  imports: [MailModule],        // 2. import the module that exports it
  providers: [UsersService],
})
export class UsersModule {}

Remove imports: [MailModule] and you get exactly the error shown above. The fix is always the pair: export from the providing module, import that module where it is needed. Custom providers registered with a string or symbol token are exported by token, as in exports: [MAILER].

Do not "fix" it by registering the provider twice

Adding MailService to the providers array of a second module makes the error disappear but creates a second, separate instance. The NestJS FAQ calls this out: remove the duplicate and import the module that owns the provider instead. Duplicates cause subtle bugs with caches, connections and in-memory state.

Cause 2: an injectable class in the imports array

Classes decorated with @Injectable() belong in providers, not imports. NestJS 12 reports this one clearly at startup:

Text
"MailService" is decorated with @Injectable() and cannot appear in the
"imports" array of a module. Please move "MailService" to the "providers"
array of the importing module instead.

Cause 3: injecting an interface or a type

TypeScript interfaces and type aliases do not exist at runtime, so Nest cannot use them as tokens. With no @Inject() decorator, the error loses its token name:

TypeScript
export interface Mailer { send(to: string): Promise<void> }

@Injectable()
export class UsersService {
  constructor(private readonly mailer: Mailer) {} // no runtime token
}
// Nest can't resolve dependencies of the UsersService (?).
// Please make sure that the argument at index [0] is available
// in the current module.

Inject a concrete class, or define a token and use @Inject(TOKEN) as in the first example. The same applies to import type { MailService }: a type-only import is erased, so Nest sees nothing. Use a normal import for any class you inject by type.

Young man seated at a large 1930 telephone switchboard with dials, meters and rows of jacks
Like a switchboard operator, the Nest injector can only connect a request to a provider that is registered and visible from the calling module.

Cause 4: a circular file import

When two files import each other, one of them sees the other's export as undefined at the moment the decorator metadata is recorded. In a CommonJS build, two services that inject each other produce:

Text
Nest can't resolve dependencies of the BService (?).
Please make sure that the argument at index [0] is available
in the current module.

In an ES module build the failure happens even earlier, before Nest starts, because the emitted metadata touches the class during module evaluation:

Text
ReferenceError: Cannot access 'AService' before initialization

Barrel files (index.ts files that re-export a folder) are a frequent hidden source of such cycles. The NestJS documentation advises avoiding barrel files for module and provider classes, and never importing a sibling through its own folder's barrel.

Cause 5: providers that genuinely depend on each other

If AService needs BService and BService needs AService, first ask whether the shared logic belongs in a third provider that both depend on. That is usually the better design. When the cycle is real, use forwardRef() on both sides:

TypeScript
// wrapper-type.ts
// Hides the real class from the emitted design:paramtypes metadata.
export type WrapperType<T> = T;

// a.service.ts
import { Inject, Injectable, forwardRef } from '@nestjs/common';
import type { WrapperType } from './wrapper-type.js';
import { BService } from './b.service.js';

@Injectable()
export class AService {
  constructor(
    @Inject(forwardRef(() => BService))
    private readonly b: WrapperType<BService>,
  ) {}
}

// b.service.ts mirrors this with forwardRef(() => AService)

In CommonJS, @Inject(forwardRef(() => BService)) with a plain BService type is enough. In an ES module build (NestJS 12's own packages now ship as ESM, and apps can opt in), the plain type still throws the ReferenceError above, because the compiler emits BService into the constructor metadata. The NestJS SWC recipe documents the WrapperType<T> workaround: the alias makes the compiler emit Object instead, while you keep full type checking.

Module-level cycles use the same idea: imports: [forwardRef(() => CatsModule)] in one module and the mirror in the other. Two warnings from the docs: the order of instantiation is indeterminate, so constructors must not call into the other provider, and cycles that involve request-scoped providers can still produce undefined dependencies. The alternative is to fetch one side lazily with ModuleRef.

If the message says "The module at index [<index>] of the <module> "imports" array is undefined", a module class was undefined when the imports array was evaluated. That is the module-level version of cause 4: a circular file import, or a constant imported from a file that imports back. Use forwardRef() for module cycles, or move shared constants to their own file.

Close-up of a green circuit board crowded with orange capacitors, blue electrolytic capacitors and chips
Circular wiring between providers works with forwardRef, but a redesign is usually the better fix.

Cause 6: two copies of @nestjs/core in a monorepo

An error naming ModuleRef as the missing argument usually means your workspace has installed two copies of @nestjs/core, for example one in a shared library package and one in the app. The FAQ's fixes: for Yarn workspaces, prevent hoisting of @nestjs/core; for pnpm workspaces, declare @nestjs/core as a peer dependency in the library and set dependenciesMeta with injected: true in the consuming app. Check with pnpm why @nestjs/core or npm ls @nestjs/core.

Quick diagnosis table

What the message showsLikely causeFix
A token name and a module nameProvider not registered, exported or importedExport from the owning module and import that module
"is decorated with @Injectable() and cannot appear in the imports array"Provider listed in importsMove it to providers
No token name, "in the current module"Interface, type-only import or circular file importUse a class or token; normal import; break the cycle
ReferenceError: Cannot access 'X' before initializationCircular import in ESM with decorator metadataforwardRef plus WrapperType<T>, or remove the cycle
"imports array is undefined"Module-level circular importforwardRef(() => Module) on both sides
ModuleRef at indexTwo copies of @nestjs/coreDeduplicate the dependency in the workspace

Gotchas

  • Decorator metadata must be on. Class-type injection relies on emitDecoratorMetadata and experimentalDecorators in tsconfig.json. Tools that strip types without emitting metadata, such as Node's built-in type stripping, cannot run Nest's constructor injection unaided.
  • Test modules need the same wiring. A Test.createTestingModule() call fails with the same error if you forget to import a module or provide a mock for a token.
  • Self-injection is not allowed. If the missing token equals the class being built, you have injected a class into itself.

What to do next

Next time the error appears, read the token, index and module before changing anything, and match them against the table. Then fix the module graph rather than adding duplicate providers. If you find yourself reaching for forwardRef() often, treat it as a design signal: extract the shared logic into a provider both sides can depend on.

Frequently asked questions

  • What does the question mark in "UsersService (?)" mean?

    Each constructor argument is listed in the brackets, and the ? marks the one Nest could not resolve. Its position matches the index in the second line of the message.

  • Why does the error say "in the current module" without a token name?

    The token was undefined at runtime. The usual causes are injecting an interface or type, a type-only import of the class, or a circular file import.

  • Should I add the provider to every module that needs it?

    No. That creates separate instances. Export the provider from the module that owns it and import that module where it is needed. Use @Global() sparingly, for genuinely application-wide providers.

  • Is forwardRef bad practice?

    It is a supported tool, but a circular dependency often signals that shared logic should move into a third provider. Use forwardRef() when the cycle is genuine, and never call the other provider from a constructor.

  • Why does forwardRef still fail in my ESM NestJS project?

    Decorator metadata emits the class reference during module evaluation, which hits the temporal dead zone in a circular ESM import. Type the parameter as WrapperType<OtherService>, as documented in the NestJS SWC recipe, so the metadata records Object instead.

  • How do I get more detail about dependency resolution?

    Set the NEST_DEBUG environment variable to any non-empty value. Nest then logs each resolution step with colour-coded class, token and module names.

Advertise on CloudSoftZone

Your next customer could start right here.

Introduce your tool, product or business to readers exploring technology.

Advertise with us

Explore placements and request a private quote

Sources

  1. Common errors — NestJS
  2. Circular dependency — NestJS
  3. Custom providers — NestJS
  4. SWC (fast compiler) — NestJS
  5. Migration guide (v11 to v12) — NestJS

Written with substantial AI assistance. Facts checked against cited sources. Editorial policy.

Comments

No comments yet. Be the first to join the conversation.

Leave a comment

Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.

Never published. Used only to contact you about this comment.

Loading the security check…

Comments are moderated before publication.