常见错误
在学习和使用 NestJS 的过程中,你可能会遇到各种各样的错误。
“Cannot resolve dependency” 错误
提示
你可以使用 NestJS Devtools,它能帮助你更轻松地定位和解决 “Cannot resolve dependency” 错误。
这通常是最常见的一类错误,表示 Nest 无法解析某个 provider 的依赖。错误信息通常类似下面这样:
Nest can't resolve dependencies of the <provider> (?). Please make sure that the argument <unknown_token> at index [<index>] is available in the <module> context.
Potential solutions:
- Is <module> a valid NestJS module?
- If <unknown_token> is a provider, is it part of the current <module>?
- If <unknown_token> is exported from a separate @Module, is that module imported within <module>?
@Module({
imports: [ /* the Module containing <unknown_token> */ ]
})最常见的原因,是没有把 <provider> 放进模块的 providers 数组。请确认该 provider 确实位于 providers 中,并遵循了标准的 NestJS provider 用法。
还有几个常见坑点:
其中一个是错误地把 provider 放进了 imports 数组。如果是这种情况,错误信息里 <module> 对应的位置往往会显示成 provider 的名字。
如果你在开发过程中遇到这个错误,可以先查看报错中提到的那个模块,然后检查它的 providers。对于 providers 数组中的每一个 provider,都要确认该模块确实能访问到其全部依赖。很常见的一种情况是:某个 provider 同时在“功能模块”和“根模块”中重复声明,导致 Nest 会尝试实例化它两次。通常更合理的做法是:把真正声明 <provider> 的那个模块加入根模块的 imports,而不是重复声明 provider。
如果上面报错中的 <unknown_token> 是 dependency,那你可能遇到的是文件级循环导入。这和下面的循环依赖不是一回事。它不是两个 provider 在构造函数中互相依赖,而是两个 TypeScript 文件彼此导入。一个典型例子是:模块文件里声明了某个 token 并导入了 provider,而这个 provider 又反过来从模块文件中导入该 token 常量。如果你使用 barrel 文件,也要确保 barrel import 没有无意中制造这种循环导入。
如果 <unknown_token> 是 Object,那说明你在注入某个类型 / 接口时,没有提供正确的 provider token。要修复它,请确认:
- 你导入的是类本身的引用,或者使用了配合
@Inject()的自定义 token。详见自定义 provider。 - 对于基于类的 provider,你导入的是具体类,而不是只通过
import type ...语法导入了类型。
另外,也请确认你没有让某个 provider 注入它自己,因为 NestJS 不允许自注入(self-injection)。发生这种情况时,<unknown_token> 往往会与 <provider> 相同。
如果你使用的是 monorepo,还有一种情况是:报错中的 <unknown_token> 会变成核心 provider ModuleRef:
Nest can't resolve dependencies of the <provider> (?).
Please make sure that the argument ModuleRef at index [<index>] is available in the <module> context.
...这通常意味着你的项目最终加载了两份 @nestjs/core,例如:
.
├── package.json
├── apps
│ └── api
│ └── node_modules
│ └── @nestjs/bull
│ └── node_modules
│ └── @nestjs/core
└── node_modules
├── (other packages)
└── @nestjs/core解决方法:
- 对于 Yarn Workspaces,使用 nohoist 防止
@nestjs/core被提升。 - 对于 pnpm Workspaces,将
@nestjs/core设为其他模块的peerDependencies,并在导入该模块的应用package.json中配置"dependenciesMeta": { "other-module-name": { "injected": true } }。详见 dependenciesMeta.injected。
“Circular dependency” 错误
有时候你会发现应用中很难完全避免循环依赖。这时需要做一些额外处理,帮助 Nest 解析它们。循环依赖报错通常类似如下:
Nest cannot create the <module> instance.
The module at index [<index>] of the <module> "imports" array is undefined.
Potential causes:
- A circular dependency between modules. Use forwardRef() to avoid it. Read more: /fundamentals/circular-dependency
- The module at index [<index>] is of type "undefined". Check your import statements and the type of the module.
Scope [<module_import_chain>]
# example chain AppModule -> FooModule循环依赖既可能来自 provider 之间的互相依赖,也可能来自 TypeScript 文件之间围绕常量等内容互相导入,例如在模块文件中导出常量,再在 service 文件中导入这些常量。
对于后一种情况,通常建议把常量提取到一个单独文件中。
对于前一种情况,请按照循环依赖章节中的指导操作,并确认模块和provider两侧都正确使用了 forwardRef()。
调试依赖错误
除了手工检查依赖关系之外,从 Nest 8.1.0 开始,你还可以将环境变量 NEST_DEBUG 设为一个可解析为真值的字符串,这样在 Nest 解析应用依赖时,会输出更多日志信息。

上图中,黄色字符串表示被注入依赖的宿主类,蓝色字符串表示被注入依赖本身的名称或注入 token,紫色字符串表示 Nest 正在其中查找该依赖的模块。借助这些信息,你通常可以追溯依赖解析路径,从而定位为什么会出现依赖注入问题。
“File change detected” 无尽循环
使用 TypeScript 4.9 及以上版本的 Windows 用户,可能会遇到这个问题。
它通常出现在你以 watch 模式运行应用时,例如执行 npm run start:dev,然后看到日志不断重复:
XX:XX:XX AM - File change detected. Starting incremental compilation...
XX:XX:XX AM - Found 0 errors. Watching for file changes.当你使用 NestJS CLI 以 watch 模式启动应用时,底层实际上调用的是 tsc --watch。而从 TypeScript 4.9 起,它引入了新的文件变更检测策略,这很可能正是问题来源。
要修复该问题,需要在 tsconfig.json 中的 "compilerOptions" 之后增加如下设置:
"watchOptions": {
"watchFile": "fixedPollingInterval"
}这会强制 TypeScript 使用轮询(polling)方式来检测文件变化,而不是使用文件系统事件(新的默认方式)。在某些机器上,后者可能会导致问题。
关于 "watchFile" 选项的更多信息,请参考 TypeScript 文档。