logo

开发排错指南

报错不可怕,可怕的是看着报错发呆 20 分钟,然后默默关掉终端假装什么都没发生。

说实话我当初也是 console.log 大法走天下,哪里不对就 console.log('here???'),一个文件插十几个,最后自己都分不清哪个是哪个。后来才慢慢摸索出一套排错的方法论——不是说 console.log 不好用,而是有更快的路子。

这份指南把实际开发中踩过的坑、常见的报错、好用的工具整理到了一起。不管你是刚入门还是已经写了一段时间代码,应该都能找到有用的东西。


排错黄金法则

排错说白了就是个缩小范围的过程。很多人一看到报错就开始瞎改代码碰运气,其实静下心来按步骤走反而更快。我自己总结下来就这么几个关键动作:

先把报错信息读完整。这话听着像废话对吧,但真的很多人看到一堆红字就慌了。报错信息一般会告诉你错误类型(TypeError、SyntaxError 这些)、具体描述、还有出错位置。有个小技巧:从最后一行开始往上看,最底下的 stack trace 通常才是你自己写的代码,上面都是框架内部调用链。

然后就是复现。能稳定复现的 bug 就已经解决了一半。问自己:每次都出现还是偶发?什么操作之后触发的?换个浏览器试过没?如果不能复现,先别急着修,把当时的环境和步骤记下来。

接下来二分法缩小范围——注释掉一半代码看问题还在不在,重复几次就能定位到具体哪几行。找到问题之后先想清楚根因再动手改,头痛医头的方案可以临时用,但记个 TODO 别让它变成永久方案。最后别忘了回去把复现路径再走一遍验证,顺便想想有没有相关的 edge case。


前端常见报错

CORS 错误

Access to XMLHttpRequest at 'xxx' from origin 'xxx' has been blocked by CORS policy

原因:浏览器的同源策略,前端域名和 API 域名不一致。

我之前有个项目,前后端分开部署,花了整整一下午才搞明白 CORS 到底在拦什么。当时后端明明设了 header,但 preflight OPTIONS 请求一直返回 403——最后发现是 Nginx 那层把 OPTIONS 请求直接拦掉了,根本没到后端。这种多层代理的情况特别容易踩坑。

怎么解决:开发环境在 next.config.jsvite.config.ts 里配 proxy 就行。后端设置 Access-Control-Allow-Origin header,但注意不要用 * 通配符上生产,指定具体域名。

Cannot read properties of undefined

TypeError: Cannot read properties of undefined (reading 'map')

这个错应该是前端出现频率最高的了。说来惭愧,我到现在偶尔还会犯——上次写了个组件,API 数据还没返回就开始 .map() 渲染列表,开发环境网速快没问题,一到测试环境网速慢了直接白屏。花了二十分钟才意识到就是少了个 loading 判断。

本质上就是访问了 undefinednull 的属性。用 optional chaining data?.items?.map(...) 或者给默认值 const items = data?.items || [],再加个 loading 状态判断就好了。

Module not found

Module not found: Can't resolve 'xxx'

依赖没装、路径写错、或者大小写不对。macOS 不区分大小写但 Linux 区分,这个坑在部署时特别常见——本地跑得好好的,CI 一跑就挂。bun install 重新装依赖,检查 import 路径大小写,实在不行删掉 node_modules 和 lock 文件重来。

Hydration Mismatch (Next.js)

Text content does not match server-rendered HTML

原因:服务端渲染的 HTML 和客户端渲染的不一致。常见于使用了 Date.now()window 对象、或者随机数。

解决

  • useEffect 处理只在客户端运行的逻辑
  • dynamic(() => import(...), { ssr: false }) 禁用特定组件的 SSR
  • 检查是否有 browser-only API 在服务端执行了

Maximum update depth exceeded

Error: Maximum update depth exceeded

组件无限循环渲染了。十有八九是 useEffect 依赖数组写错,或者在 render 过程中直接调了 setState。检查依赖数组是不是每次渲染都创建了新的引用(比如在 render 里 new Object())。还有一个经典错误:onClick={setCount(count + 1)} 少了箭头函数包裹,应该写 onClick={() => setCount(count + 1)}

白屏 / Chunk Load Error

ChunkLoadError: Loading chunk xxx failed

部署新版本后,用户浏览器缓存了旧的 chunk 文件名,但服务器上已经换成新的了。加个全局错误边界 catch 住这个错误后自动刷新页面,再配合合理的缓存策略基本就能解决。

样式不生效 & React Key Warning

样式问题原因千奇百怪:CSS 优先级、class 名冲突、styled-components 的 SSR 配置没弄对。排查时打开 DevTools Elements 面板看样式有没有被删除线划掉(说明被更高优先级覆盖了),styled-components 记得配 ServerStyleSheet

至于 Each child in a list should have a unique "key" prop 这个警告,用数据的唯一 ID 当 key 就行,别用数组 index——尤其是列表会增删排序的情况下,index 当 key 会导致渲染异常。

环境变量和类型错误

Next.js 里客户端能访问的环境变量必须以 NEXT_PUBLIC_ 开头,服务端变量不需要这个前缀。改完 .env 文件记得重启 dev server,这一点很容易忘。

TypeScript 类型错误碰到 Type 'string' is not assignable to type 'number' 这种,别急着加 as anyas any 是在欺骗编译器,bug 会在运行时找你算账。先想想是不是数据结构真的和预期不一样。


后端常见报错

EADDRINUSE (端口被占用)

Error: listen EADDRINUSE: address already in use :::3010
# 找到占用端口的进程然后杀掉
lsof -i :3010
kill -9 <PID>

ECONNREFUSED / 500 Internal Server Error

ECONNREFUSED 127.0.0.1:27017 就是数据库或目标服务没启动,检查 MongoDB / Redis 是否在跑。

500 是个万金油错误码,意思就是"后端炸了但不告诉你为什么"。前端只拿到 500 是正常的(敏感错误不应该暴露给客户端),排查得去看后端日志,错误详情一定在那里。

401 / 403 认证问题

  • 401 Unauthorized:没带 token 或 token 过期了
  • 403 Forbidden:token 有效但没权限

用 DevTools Network 面板看 request headers 里有没有 Authorization: Bearer xxx。JWT 相关的 jwt malformedjwt expired 也归这一类,检查 token 有没有被截断,过期了就重新登录。

MongoDB Duplicate Key Error

E11000 duplicate key error collection

插入了重复的 unique 字段值。检查是不是有脏数据,或者 unique index 建错了。这个在批量导入数据的时候特别常见。

Mongoose Validation Error

ValidationError: xxx: Path `xxx` is required

必填字段没传,对照 Schema 定义检查请求体就行。简单但容易忘——尤其是后来新加的 required 字段,老的测试数据就会挂。

Memory Leak / Heap Out of Memory

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory

临时方案 NODE_OPTIONS=--max-old-space-size=4096 能顶一阵,但治标不治本。根本上要排查内存泄漏——常见原因是没清理的 setInterval、事件监听器忘了 removeListener、或者大数组不断往里 push 没有上限。

Module 循环依赖 & Timeout

NestJS 里两个 module 互相 import 会导致 Cannot read property 'xxx' of undefined。用 forwardRef(() => XxxModule) 打破循环,或者重新设计模块依赖。

Timeout 错误一般是数据库查询太慢、外部 API 响应慢、或者死循环,给关键操作加耗时日志找出瓶颈在哪一步。


AI 辅助排错

ChatGPT 和 Claude 是排错利器,但很多人用法不对,丢个截图过去问"这是什么错",得到的回答也是泛泛而谈。

高效提问模板

我在做 [具体功能],用的技术栈是 [框架版本]。

执行 [具体操作] 时出现了这个错误:

[完整报错信息,不要截图,复制文字]

我已经试过:
1. [你尝试过的方案 1]
2. [你尝试过的方案 2]

相关代码:
[贴出关键代码片段,不要贴整个文件]

几个技巧

  • 贴文字不要贴截图:AI 解析文字比解析图片准确得多
  • 说清版本号:React 18 和 React 19 的解决方案可能完全不同
  • 说明你试过什么:避免 AI 重复建议你已经试过的方案
  • 给上下文:是 Next.js 的 App Router 还是 Pages Router?是 NestJS 还是 Express?这些都会影响答案

排错工具链

浏览器 DevTools

最强大的前端调试工具,几个必会的面板:

Chrome DevTools Console 面板

  • Console:看报错和日志
  • Network:看请求状态码、响应内容、请求耗时
  • Elements:看 DOM 结构和 CSS 样式
  • Application:看 localStorage、cookies、缓存
  • Sources:打断点,比 console.log 精准多了

VS Code Debugger

配置 launch.json 之后可以直接在编辑器里打断点调试 Node.js 代码,能看到完整的调用栈和变量值。比在代码里到处插 console.log 好用太多。

VS Code Debugger 断点调试

Postman / Thunder Client

API 调试的标配工具。建议把常用请求保存成 Collection,团队共享。前后端联调的时候,先用 Postman 确认 API 没问题,再排查前端。

终端日志

后端排错第一步永远是看日志。NestJS 的 Logger 已经很好用了,别嫌麻烦,关键操作都加上日志:

this.logger.log(`Creating user: ${email}`);
this.logger.error(`Failed to create user: ${error.message}`, error.stack);

最容易翻车的排错习惯

我自己就经常犯第4条的毛病,改了代码忘了保存,然后对着旧的输出纳闷半天。下面这几条你看看有没有中招的:

只看第一行报错。很多报错连续输出好几十行,真正有用的信息可能在中间或最后。尤其是 stack trace,你自己写的代码一般在最下面,上面都是框架内部的调用链。

不看版本号。"我按教程写的,为什么不行?"——因为教程用的 v2,你装的 v3,API 早就变了。永远先检查 package.json

盲目抄 Stack Overflow。上面的回答可能是 2018 年写的,那个年代的方案现在可能早过时了。看的时候留意发布时间和评论区有没有人说"这个已经不行了"。

改了代码没保存 / 没重启。经典中的经典。改了后端代码忘了重启 dev server,改了 .env 没重启,改了配置但看的还是旧缓存。我有一次 debug 了快一个小时,最后发现改的那个文件根本没 Cmd+S……

多个问题同时改。一次只改一个地方,验证完再改下一个。同时改了三个地方然后好了,你都不知道到底是哪个改动修复了问题。

不用版本控制。改代码前先 git stash 或者开个新分支。万一改崩了,git stash pop 就能回到之前的状态。别让一次排错变成两个 bug。


相关资源

开发者常见问题排查指南
AI Engineer

开发者常见问题排查指南

汇总开发中常见的报错与问题排查方法,涵盖环境配置、包管理、构建工具等。

开发者常见问题排查指南排查指南简介

开发排错指南

报错不可怕,可怕的是看着报错发呆 20 分钟,然后默默关掉终端假装什么都没发生。

说实话我当初也是 console.log 大法走天下,哪里不对就 console.log('here???'),一个文件插十几个,最后自己都分不清哪个是哪个。后来才慢慢摸索出一套排错的方法论——不是说 console.log 不好用,而是有更快的路子。

这份指南把实际开发中踩过的坑、常见的报错、好用的工具整理到了一起。不管你是刚入门还是已经写了一段时间代码,应该都能找到有用的东西。


#排错黄金法则

排错说白了就是个缩小范围的过程。很多人一看到报错就开始瞎改代码碰运气,其实静下心来按步骤走反而更快。我自己总结下来就这么几个关键动作:

先把报错信息读完整。这话听着像废话对吧,但真的很多人看到一堆红字就慌了。报错信息一般会告诉你错误类型(TypeError、SyntaxError 这些)、具体描述、还有出错位置。有个小技巧:从最后一行开始往上看,最底下的 stack trace 通常才是你自己写的代码,上面都是框架内部调用链。

然后就是复现。能稳定复现的 bug 就已经解决了一半。问自己:每次都出现还是偶发?什么操作之后触发的?换个浏览器试过没?如果不能复现,先别急着修,把当时的环境和步骤记下来。

接下来二分法缩小范围——注释掉一半代码看问题还在不在,重复几次就能定位到具体哪几行。找到问题之后先想清楚根因再动手改,头痛医头的方案可以临时用,但记个 TODO 别让它变成永久方案。最后别忘了回去把复现路径再走一遍验证,顺便想想有没有相关的 edge case。


#前端常见报错

#CORS 错误

Access to XMLHttpRequest at 'xxx' from origin 'xxx' has been blocked by CORS policy

原因:浏览器的同源策略,前端域名和 API 域名不一致。

我之前有个项目,前后端分开部署,花了整整一下午才搞明白 CORS 到底在拦什么。当时后端明明设了 header,但 preflight OPTIONS 请求一直返回 403——最后发现是 Nginx 那层把 OPTIONS 请求直接拦掉了,根本没到后端。这种多层代理的情况特别容易踩坑。

怎么解决:开发环境在 next.config.jsvite.config.ts 里配 proxy 就行。后端设置 Access-Control-Allow-Origin header,但注意不要用 * 通配符上生产,指定具体域名。

#Cannot read properties of undefined

TypeError: Cannot read properties of undefined (reading 'map')

这个错应该是前端出现频率最高的了。说来惭愧,我到现在偶尔还会犯——上次写了个组件,API 数据还没返回就开始 .map() 渲染列表,开发环境网速快没问题,一到测试环境网速慢了直接白屏。花了二十分钟才意识到就是少了个 loading 判断。

本质上就是访问了 undefinednull 的属性。用 optional chaining data?.items?.map(...) 或者给默认值 const items = data?.items || [],再加个 loading 状态判断就好了。

#Module not found

Module not found: Can't resolve 'xxx'

依赖没装、路径写错、或者大小写不对。macOS 不区分大小写但 Linux 区分,这个坑在部署时特别常见——本地跑得好好的,CI 一跑就挂。bun install 重新装依赖,检查 import 路径大小写,实在不行删掉 node_modules 和 lock 文件重来。

#Hydration Mismatch (Next.js)

Text content does not match server-rendered HTML

原因:服务端渲染的 HTML 和客户端渲染的不一致。常见于使用了 Date.now()window 对象、或者随机数。

解决

  • useEffect 处理只在客户端运行的逻辑
  • dynamic(() => import(...), { ssr: false }) 禁用特定组件的 SSR
  • 检查是否有 browser-only API 在服务端执行了

#Maximum update depth exceeded

Error: Maximum update depth exceeded

组件无限循环渲染了。十有八九是 useEffect 依赖数组写错,或者在 render 过程中直接调了 setState。检查依赖数组是不是每次渲染都创建了新的引用(比如在 render 里 new Object())。还有一个经典错误:onClick={setCount(count + 1)} 少了箭头函数包裹,应该写 onClick={() => setCount(count + 1)}

#白屏 / Chunk Load Error

ChunkLoadError: Loading chunk xxx failed

部署新版本后,用户浏览器缓存了旧的 chunk 文件名,但服务器上已经换成新的了。加个全局错误边界 catch 住这个错误后自动刷新页面,再配合合理的缓存策略基本就能解决。

#样式不生效 & React Key Warning

样式问题原因千奇百怪:CSS 优先级、class 名冲突、styled-components 的 SSR 配置没弄对。排查时打开 DevTools Elements 面板看样式有没有被删除线划掉(说明被更高优先级覆盖了),styled-components 记得配 ServerStyleSheet

至于 Each child in a list should have a unique "key" prop 这个警告,用数据的唯一 ID 当 key 就行,别用数组 index——尤其是列表会增删排序的情况下,index 当 key 会导致渲染异常。

#环境变量和类型错误

Next.js 里客户端能访问的环境变量必须以 NEXT_PUBLIC_ 开头,服务端变量不需要这个前缀。改完 .env 文件记得重启 dev server,这一点很容易忘。

TypeScript 类型错误碰到 Type 'string' is not assignable to type 'number' 这种,别急着加 as anyas any 是在欺骗编译器,bug 会在运行时找你算账。先想想是不是数据结构真的和预期不一样。


#后端常见报错

#EADDRINUSE (端口被占用)

Error: listen EADDRINUSE: address already in use :::3010
bash
# 找到占用端口的进程然后杀掉 lsof -i :3010 kill -9 <PID>

#ECONNREFUSED / 500 Internal Server Error

ECONNREFUSED 127.0.0.1:27017 就是数据库或目标服务没启动,检查 MongoDB / Redis 是否在跑。

500 是个万金油错误码,意思就是"后端炸了但不告诉你为什么"。前端只拿到 500 是正常的(敏感错误不应该暴露给客户端),排查得去看后端日志,错误详情一定在那里。

#401 / 403 认证问题

  • 401 Unauthorized:没带 token 或 token 过期了
  • 403 Forbidden:token 有效但没权限

用 DevTools Network 面板看 request headers 里有没有 Authorization: Bearer xxx。JWT 相关的 jwt malformedjwt expired 也归这一类,检查 token 有没有被截断,过期了就重新登录。

#MongoDB Duplicate Key Error

E11000 duplicate key error collection

插入了重复的 unique 字段值。检查是不是有脏数据,或者 unique index 建错了。这个在批量导入数据的时候特别常见。

#Mongoose Validation Error

ValidationError: xxx: Path `xxx` is required

必填字段没传,对照 Schema 定义检查请求体就行。简单但容易忘——尤其是后来新加的 required 字段,老的测试数据就会挂。

#Memory Leak / Heap Out of Memory

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory

临时方案 NODE_OPTIONS=--max-old-space-size=4096 能顶一阵,但治标不治本。根本上要排查内存泄漏——常见原因是没清理的 setInterval、事件监听器忘了 removeListener、或者大数组不断往里 push 没有上限。

#Module 循环依赖 & Timeout

NestJS 里两个 module 互相 import 会导致 Cannot read property 'xxx' of undefined。用 forwardRef(() => XxxModule) 打破循环,或者重新设计模块依赖。

Timeout 错误一般是数据库查询太慢、外部 API 响应慢、或者死循环,给关键操作加耗时日志找出瓶颈在哪一步。


#AI 辅助排错

ChatGPT 和 Claude 是排错利器,但很多人用法不对,丢个截图过去问"这是什么错",得到的回答也是泛泛而谈。

#高效提问模板

我在做 [具体功能],用的技术栈是 [框架版本]。

执行 [具体操作] 时出现了这个错误:

[完整报错信息,不要截图,复制文字]

我已经试过:
1. [你尝试过的方案 1]
2. [你尝试过的方案 2]

相关代码:
[贴出关键代码片段,不要贴整个文件]

#几个技巧

  • 贴文字不要贴截图:AI 解析文字比解析图片准确得多
  • 说清版本号:React 18 和 React 19 的解决方案可能完全不同
  • 说明你试过什么:避免 AI 重复建议你已经试过的方案
  • 给上下文:是 Next.js 的 App Router 还是 Pages Router?是 NestJS 还是 Express?这些都会影响答案

#排错工具链

#浏览器 DevTools

最强大的前端调试工具,几个必会的面板:

Chrome DevTools Console 面板
Chrome DevTools Console 面板

  • Console:看报错和日志
  • Network:看请求状态码、响应内容、请求耗时
  • Elements:看 DOM 结构和 CSS 样式
  • Application:看 localStorage、cookies、缓存
  • Sources:打断点,比 console.log 精准多了

#VS Code Debugger

配置 launch.json 之后可以直接在编辑器里打断点调试 Node.js 代码,能看到完整的调用栈和变量值。比在代码里到处插 console.log 好用太多。

VS Code Debugger 断点调试
VS Code Debugger 断点调试

#Postman / Thunder Client

API 调试的标配工具。建议把常用请求保存成 Collection,团队共享。前后端联调的时候,先用 Postman 确认 API 没问题,再排查前端。

#终端日志

后端排错第一步永远是看日志。NestJS 的 Logger 已经很好用了,别嫌麻烦,关键操作都加上日志:

typescript
this.logger.log(`Creating user: ${email}`); this.logger.error(`Failed to create user: ${error.message}`, error.stack);

#最容易翻车的排错习惯

我自己就经常犯第4条的毛病,改了代码忘了保存,然后对着旧的输出纳闷半天。下面这几条你看看有没有中招的:

只看第一行报错。很多报错连续输出好几十行,真正有用的信息可能在中间或最后。尤其是 stack trace,你自己写的代码一般在最下面,上面都是框架内部的调用链。

不看版本号。"我按教程写的,为什么不行?"——因为教程用的 v2,你装的 v3,API 早就变了。永远先检查 package.json

盲目抄 Stack Overflow。上面的回答可能是 2018 年写的,那个年代的方案现在可能早过时了。看的时候留意发布时间和评论区有没有人说"这个已经不行了"。

改了代码没保存 / 没重启。经典中的经典。改了后端代码忘了重启 dev server,改了 .env 没重启,改了配置但看的还是旧缓存。我有一次 debug 了快一个小时,最后发现改的那个文件根本没 Cmd+S……

多个问题同时改。一次只改一个地方,验证完再改下一个。同时改了三个地方然后好了,你都不知道到底是哪个改动修复了问题。

不用版本控制。改代码前先 git stash 或者开个新分支。万一改崩了,git stash pop 就能回到之前的状态。别让一次排错变成两个 bug。


#相关资源

Vibe Coding

AI 编程体系课:工具、流程与最佳实践

从零搭建 AI 编程工作流,提升开发效率。

进入 Vibe Coding →

相关路线图

常见问题

遇到报错应该如何排查?
先看错误信息,搜索关键词,检查版本兼容性,查看官方 Issue。80% 的问题已经有人遇到过了。
常见的环境问题有哪些?
Node.js 版本不匹配、Python 虚拟环境未激活、包版本冲突、端口占用是最常见的四类。