recover() 只在 defer 函数中且 panic 后同一 goroutine 未退出前调用才有效;若 defer 位置错误、跨 goroutine、类型断言不当或 http handler 中 goroutine 分离,均会导致拿不到 panic 值。

recover() 拿不到 panic 值?先确认 defer 执行时机
Go 的 recover() 只在 defer 函数中调用才有效,且必须在 panic 发生后的同一 goroutine 中、尚未退出函数前执行。如果 defer 写在 panic 之后的代码里(比如写在 if 分支里),或者放在已 return 的路径后,recover() 会返回 nil —— 这是最常见的“拿不到值”原因。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
-
defer必须写在可能 panic 的代码之前,通常放在函数开头最稳妥 - 不要把
recover()放在独立函数里调用(比如defer handleRecover()),除非那个函数内部直接调用了recover() - panic 后若被其他 goroutine 的 recover 捕获?不可能 ——
recover()只对本 goroutine 有效
recover() 返回值类型是 interface{},不是 error
很多人误以为 recover() 返回 error,结果直接断言 err.(error) 导致二次 panic。其实它返回的是当时传给 panic() 的任意值,类型完全取决于你 panic 时传了什么。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用类型断言前先判断是否为
nil:if r := recover(); r != nil { ... } - 想统一处理?可以接受
interface{},再用fmt.Sprintf("%v", r)转成字符串记录日志 - 如果 panic 传的是
string,r.(string)安全;但若传了struct{}或nil,强转error就会崩溃
HTTP handler 中 recover 失效?检查中间件和 goroutine 分离
Web 服务里常见:主 handler 里写了 defer recover(),但 panic 还是冒泡到服务器层,返回 500。问题往往出在两个地方:一是 panic 发生在子 goroutine(比如 go fn()),二是框架中间件提前 return 或重写了 panic 流程。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- HTTP handler 内部启动的 goroutine 必须自己加
defer recover(),父函数的 defer 对它无效 - 用
http.Server的ErrorLog配置可捕获未被 recover 的 panic 日志,方便定位漏网之鱼 - 某些路由框架(如 Gin)自带 recovery 中间件,此时你自己写的 defer 可能被绕过 —— 查文档确认执行顺序
recover 后程序继续运行,但状态可能已损坏
recover 不等于“错误已修复”。它只是阻止 panic 终止 goroutine,但 panic 前的内存修改、锁持有、channel 关闭等状态不会自动回滚。强行继续执行常导致数据不一致或死锁。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- recover 后尽量只做清理和记录,然后主动 return 或调用
os.Exit(1)(尤其在 main goroutine) - 不要在 recover 后继续使用可能已被破坏的全局变量或共享结构体
- 数据库事务、文件句柄、网络连接等资源,应在 defer 中用独立逻辑关闭,别依赖 recover 后的“补救代码”
真正难的不是拿到 panic 值,而是判断这个 goroutine 还有没有安全继续跑下去的余地。多数时候,log 一下,停掉当前请求,比硬扛更靠谱。











