recover仅在defer函数体内直接调用且panic前注册才有效;必须写在defer匿名函数第一层,panic后程序从defer返回后继续执行后续语句(若存在),goroutine间panic不互通。

recover 只有在 defer 函数体内直接调用才有效,且必须在 panic 发生前注册 defer;否则永远捕获不到。
recover 必须写在 defer 匿名函数里,不能提前或外包
常见错误是把 recover() 放在 panic() 之后、或包在另一层函数里,比如:
-
panic("x"); defer func() { recover() }()—— defer 还没注册,panic 已经展开栈 -
defer func() { go func() { recover() }() }()——recover()不在 defer 直接作用域内,返回nil -
defer recover()—— 语法错误,recover不是可 defer 的函数值
正确姿势只有一种:defer func() 立即定义并注册,recover() 在其函数体第一层调用:
func f() {
defer func() {
if r := recover(); r != nil {
log.Println("caught:", r)
}
}()
panic("boom")
}
panic 后函数立即 return,后续语句不执行
recover() 成功后,程序不会回到 panic 那行重试,而是从 defer 函数返回后继续执行该函数的**下一条语句(如果有的话)**。但注意:panic 当前行之后的代码全被跳过。
OpenClaw 基础设施心跳监控与自动恢复:部署/卸载心跳守护进程,配置主机监控(Gateway、磁盘、内存、CPU、进程),设置飞书告警,自动重启故障Gateway。适用关键词:心跳监控、heartbeat daemon、服务器告警、自动重启、健康检查、auto recovery。
立即学习“go语言免费学习笔记(深入)”;
- 下面的
fmt.Println("never printed")永远不会输出 - 已发生的副作用(如
map写入、channel发送)无法回滚
func example() {
defer func() { recover() }()
panic("stop here")
fmt.Println("never printed") // ← 这行被跳过
}
goroutine 之间 panic 不互通,每个都要自己加 recover
主线程里写 defer+recover,对 go func() { panic("x") }() 完全无效。子 goroutine panic 后静默退出,主 goroutine 不知情。
- HTTP handler 中启 goroutine 做异步任务?必须在 goroutine 内部自己加
defer func() { recover() }() - 封装
GoWithRecover(fn func(), errCh chan 是更可靠的做法,避免漏写 - 恢复后建议用
debug.Stack()打印完整堆栈,否则只看到 panic 值,难定位
recover 不是 error 替代品,只处理真正意外的编程错误
除零、空指针解引用、切片越界这类不可预判的运行时错误,才适合用 panic/recover。业务逻辑错误(如参数校验失败、数据库查无结果)必须用 error 显式返回 + if err != nil 处理。
- 滥用
recover会掩盖真实问题,让错误日志缺失、状态不一致 - 它只负责“不崩溃”,不负责“修复”或“降级”,后续逻辑需靠显式分支控制
最易忽略的一点:recover 后的代码仍运行在原 goroutine 栈帧中,若 panic 前已修改共享状态(如全局 map、未加锁变量),那些变更不会自动撤销——你得自己判断是否还能安全继续。

















