recover 对 json.encoder.encode 无效,因其不 panic 而返回 error;真正需 recover 的场景包括自定义 marshaljson panic、未初始化指针、底层 writer 异常;应优先检查 err 并校验输入,encoder 错误状态不可恢复。

recover 不能直接保护 json.Encoder.Encode
因为 json.Encode 是同步、无 panic 的纯函数调用,它内部不抛出 panic,而是返回 error。你写 defer recover() 在它的外层是无效的——根本不会触发。Go 的 json 包设计原则就是「错误即值」,所有异常都走 err != nil 分支,不是靠 panic 传播。
哪些地方才真需要 recover
真正可能 panic 的环节,往往是你自己写的序列化逻辑,比如:
- 结构体字段是未初始化的指针,且没加
omitempty或空值检查 - 自定义
MarshalJSON方法里手动调用了panic("xxx") - 在
Encode前往io.Writer写入时,底层 writer(如网络连接、关闭的文件)引发 panic(极少见,但自定义 writer 可能)
这些场景下,recover 才有意义。但要注意:一旦发生 panic,Encoder 内部状态已不可靠,不能再复用该实例。
三层自动备份:每日时间戳快照、次级硬盘镜像、紧急对话导出。
正确做法:优先处理 error,而非依赖 recover
编码失败的主因永远是数据非法或 writer 不可用,不是运行时 panic。你应该:
- 每次调用
enc.Encode(v)后立即检查err,不要忽略 - 对输入结构体做前置校验(例如非空字段、有效枚举值),避免传入 nil 指针或循环引用
- 如果 writer 是网络连接或管道,用
if errors.Is(err, io.ErrClosedPipe)等明确判断并退出,而不是等 panic - 若真要兜底,只在顶层 HTTP handler 或 RPC 方法中用
defer func(){ if r := recover(); r != nil { log.Printf("panic in encoder: %v", r) } }(),且仅用于日志和降级,不尝试恢复编码流程
容易被忽略的关键点
json.Encoder 是有内部缓冲的,一旦 Write 失败(比如磁盘满、socket 断开),后续再调用 Encode 会持续返回同一个 err,但不会 panic —— 这个错误状态会一直卡住,直到你新建一个 Encoder 实例。别指望 recover 能“清空”这个状态。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










