VSCode调试await抛错不进catch的根本原因是source map未生效:需在launch.json设"sourceMaps": true并确保.map文件存在,ESM项目加"--enable-source-maps",TS项目配"module": "ESNext"且Node版本≥v16。

VSCode 调试时 await 抛错不进 catch 是因为 source map 未生效
断点能设上、但异常一抛就跳出调试器,甚至控制台只显示 UnhandledPromiseRejectionWarning,根本不是代码逻辑问题,而是 VSCode 没把转译后的 JS 映射回原始 async 函数位置。尤其在 TypeScript 或 Babel 项目中,tsc 或 babel 输出的代码里 await 已被展开为 Promise 链,调试器看不到原始结构。
-
launch.json必须含"sourceMaps": true,且确保编译产物目录(如./dist)下存在对应.js.map文件 - ESM 项目(
type: "module")额外加"runtimeArgs": ["--enable-source-maps"],Node.js ≥ v14.18.0 才支持 - 别用
outFiles做路径匹配——它对嵌套路径、符号链接极敏感;改用resolveSourceMapLocations配 glob 排除node_modules
Node.js 版本差异直接影响 async 错误堆栈完整性
v14 在复杂 Promise 链中常丢失中间帧,v16–v20 则修复了多数 await 后的堆栈截断问题。VSCode 调试器依赖 Node 的 V8 引擎提供堆栈信息,版本不一致会导致 catch 块内 err.stack 缺少关键行号或函数名。
- 不要硬编码
runtimeExecutable(如/usr/bin/node),它会绕过nvm或nodenv,导致调试器和终端运行环境不一致 - 改用
"runtimeVersion": "18.17.0"(需 VSCode ≥ 1.82),让编辑器自动匹配 PATH 中已安装的对应版本 - 若项目用
ts-node或nodemon启动,必须设runtimeExecutable指向它们,并确认其底层 Node 版本 ≥ v16
TS + ESM 组合下 await 异常捕获失效的典型配置陷阱
TypeScript 默认输出 CommonJS,但 package.json 里写了 "type": "module",就会导致 import 报错、source map 加载失败、catch 无法定位到原始 async 函数三连崩。
-
tsconfig.json至少要配:"module": "ESNext"、"target": "ES2020"、"sourceMap": true、"outDir": "./dist" -
launch.json禁用"protocol": "inspector"(旧协议不兼容 ESM),保留默认"protocol": "auto" - 入口文件后缀必须是
.mjs,或确保package.json有"type": "module",否则 Node 仍按 CJS 解析,source map不加载
异常实际被捕获但没反应?检查 unhandledrejection 监听与日志输出
VSCode 调试器不会拦截全局 unhandledrejection 事件,如果 catch 块里只做 console.error 但没 throw 或 return,错误看似“消失”,其实是被吞了。更隐蔽的是:某些日志库(如 pino)默认异步写入,调试时断点停住后日志还没刷出。
- 在
process.on('unhandledRejection', ...)里加断点,确认异常是否真的漏网 -
catch块中避免仅调用异步日志方法;改用同步输出(如console.error)或加await logger.flush()(若支持) - Node.js v15+ 默认开启
--trace-uncaught,可在launch.json的runtimeArgs中显式加上,强制打印完整堆栈
await 行抛错却进不了 catch,大概率不是语法或逻辑问题,而是 source map、模块系统、Node 版本这三层中有一层没对齐。尤其 TS + ESM 项目,tsconfig.json 和 launch.json 的任意一个字段配错,都会让异常处理变成黑盒。


















