断点不触发需先确认请求是否进入中间件链:检查express.json()等中间件是否因body错误提前返回400、鉴权中间件是否return终结响应;确保launch.json的program指向可执行.js文件,typescript项目配sourcemap并指向dist输出,esm项目注意node版本与loader配置;推荐使用attach模式而非launch,并验证调试端口唯一性及环境变量(如port)是否正确注入。

断点不触发?先验证请求是否进入中间件链
Express/Koa 里设了断点却从不停,大概率不是调试器失灵,是请求压根没走到那行代码。比如 express.json() 因 body 超限或格式错误直接抛出 400 错误,后续所有中间件和路由都不会执行;又或者某个鉴权中间件写了 return res.status(401).end() 提前终结响应链。
实操建议:
- 在第一个中间件(比如日志中间件)里加
console.log('middleware hit')或设断点,用curl -v http://localhost:3000/api/users触发请求,看终端是否有输出;没日志 = 请求连入口都没进 - 检查
app.set('trust proxy', true)是否误配,导致req.ip解析失败,进而让下游中间件(如频率限制、白名单)直接 return - 把错误处理中间件
app.use((err, req, res, next) => { ... })放在所有app.use之后,再在里面设断点,确认是否有未捕获异常提前中断流程
launch.json 的 program 必须指向可执行的 .js 文件
VSCode 不会编译 TypeScript,也不理解 ESM 的 import 语法。program 字段只能填能被 node 直接运行的路径。写成 "${workspaceFolder}/src/server.ts" 或 "${workspaceFolder}/index.mjs" 都会静默失败——调试器启动后没报错,但断点就是不生效。
实操建议:
- TypeScript 项目:要么用
tsc编译出dist/,再把program指向"${workspaceFolder}/dist/server.js",并确保tsconfig.json中"sourceMap": true - ESM 项目:Node ≥18.17 且
package.json有"type": "module",则program可指向.mjs,但必须同步在launch.json中加"runtimeArgs": ["--loader", "ts-node/esm"](若用 ts-node) - 用
npm start启动的项目,别硬写program;改用"runtimeExecutable": "npm"+"args": ["start"],避免入口路径和实际运行逻辑脱节
用 attach 模式比 launch 更稳,尤其对 Web 服务
Express/Koa 这类监听端口的长期运行服务,request: "launch" 容易卡在端口占用、环境变量隔离、子进程 fork 等问题上;而 request: "attach" 是你手动起服务,VSCode 再连上去,控制权更明确。
实操建议:
- 终端执行:
node --inspect-brk ./bin/www(注意加--inspect-brk让它停在第一行),再在 VSCode 里选 attach 配置启动 -
launch.json中"port"必须和启动命令一致:如果用了--inspect=9230,配置里就得写"port": 9230,否则连不上 - Windows 下 cmd 有时连不上,换成 PowerShell;Mac/Linux 若连不上,试试
--inspect=0.0.0.0:9229并配"address": "0.0.0.0" - 别同时跑两个
--inspect进程,第二个会报Could not connect to debug target
中间件异常时,调试器可能根本没机会介入
某些错误发生在 Node 启动阶段或模块加载期(比如 require() 失败、process.env 缺失导致配置解析崩溃),此时服务甚至没走到 app.listen(),调试器自然无法注入。这类问题在 attach 模式下更难捕获,因为进程压根没起来。
实操建议:
- 启动时加
--trace-warnings或--throw-deprecation,把隐性警告变成显性错误,方便定位早期失败点 - 在入口文件顶部加
console.log('server starting...'),确认是否执行到这一行;没输出 = 卡在 require 或顶层代码 - 检查
process.env.PORT是否被正确读取,尤其当代码里写了app.listen(process.env.PORT || 3000)却忘了在 launch.json 里配"env": { "PORT": "3001" },会导致端口冲突或绑定失败











