go结构体必须实现error() string方法才能被识别为error,否则编译报错;需避免敏感信息泄露、确保meta可序列化,并通过errorcode等函数安全提取字段。

Go 结构体错误必须实现 error 接口才能被识别
Go 的错误处理依赖接口契约,不是类型继承。只要结构体实现了 Error() 方法并返回 string,它就是合法的 error。不实现这个方法,哪怕叫 MyError、字段再全,if err != nil 也永远进不去——编译器直接报错:不能用作 error 类型。
常见错误是只定义了字段(比如 Code、Meta),却忘了加 Error() 方法;或者方法签名写成 error()(小写)或带额外参数,导致接口不满足。
-
Error()必须是无参、返回string的指针方法(推荐),否则值接收者在某些场景下可能丢失字段更新 - 不要在
Error()里拼接敏感元数据(如用户 token、原始 SQL),日志或调试时容易泄露 - 如果元数据含非字符串字段(如
time.Time、map[string]interface{}),Error()返回值应做安全序列化(如只取 key 和 string 值),避免 panic
如何让错误码和元数据可提取而不破坏标准错误链
Go 1.13+ 支持错误链(errors.Is/errors.As),但前提是你的结构体错误支持 Unwrap() 或嵌套其他 error。如果只是单纯携带 Code 和 Meta,不嵌套底层错误,就不用实现 Unwrap();但如果想兼容链式调用(比如包装 io.EOF),就得显式委托。
更关键的是「可提取」:下游代码要能安全拿到 Code 或 Meta,不能靠类型断言裸奔(e.(MyError).Code),因为一旦类型变了就 panic。
立即学习“go语言免费学习笔记(深入)”;
- 提供一个标准提取函数,比如
ErrorCode(err error) int,内部用errors.As尝试转成你的错误类型 -
Meta字段建议定义为map[string]string而非map[string]interface{},避免运行时类型断言失败 - 若需支持多级元数据(如 traceID、requestID 分离),可用嵌套结构体,但
Error()输出仍应扁平化,避免日志里出现map[0xc000123456:...]这种不可读内容
fmt.Errorf 包装时丢失自定义字段怎么办
直接用 fmt.Errorf("failed: %w", myErr) 是安全的——只要 myErr 实现了 Unwrap(),错误链就保住了。但很多人误以为 %w 会“合并”字段,结果发现新错误里 Code 没了、Meta 空了。这是因为 fmt.Errorf 创建的是新错误实例,它不关心你原结构体里的字段,只认 Unwrap() 返回的底层 error。
真正的问题在于:你想保留上下文语义(比如加前缀 “DB timeout: ”),又不想丢掉原始错误码和元数据。
- 不要用
fmt.Errorf包装后再强转回原类型;应该用新结构体错误类型封装,例如NewWrappedError(original, "DB timeout") - 如果必须用
fmt.Errorf,且下游需要访问原始字段,那就得在包装前把Code和关键Meta提取出来,单独传参或记录到日志上下文,而不是指望错误对象自己“记住” -
%v或%+v打印时能看到所有字段,但这只是调试用,不能作为运行时提取逻辑的依据
JSON 序列化结构体错误时字段为空或 panic
结构体错误常被塞进 HTTP 响应体(如 json.Marshal(resp{Err: myErr})),但默认导出规则会让未导出字段(小写开头)被忽略,而导出字段若含非 JSON 友好类型(如 func、chan、未导出内嵌结构)就会 panic。
最典型的是把 error 字段设为 Err error 并导出,结果 json.Marshal 递归进去又遇到不支持的类型。
- 错误结构体里不要直接嵌套
error类型字段;如需保留原始错误,用cause error(小写) +Unwrap(),这样 JSON 不会碰它 -
Meta若为map[string]interface{},务必确保所有 value 都是 JSON 可序列化类型(string、int、bool、nil、其他 map/slice) - 加一个
MarshalJSON()方法,手动控制输出:只序列化Code、Message、Meta(已过滤),忽略cause和函数字段
结构体错误最难的不是定义,而是让错误码和元数据在各种上下文(log、http、grpc、errors.Is)里始终可访问、不歧义、不泄露。多数坑都出在“我以为它会自动传递”,其实每个环节都要明确谁负责提取、谁负责展示、谁负责透传。










