go中第三方库错误未实现error接口时,需为其添加error()方法或适配包装;pkg/errors堆栈不可转为标准error保留;driver.errbadconn等应使用errors.is判断;跨服务错误需边界翻译而非直接序列化。

Go 中第三方库错误没实现 error 接口怎么办
很多老的或 C 绑定的 Go 库(比如 cgo 包装的 SQLite、libgit2)返回的是自定义结构体指针,而非实现了 error 接口的类型。直接用 if err != nil 会编译失败。
根本原因不是“它不是 error”,而是它没实现 Error() string 方法。Go 的 error 是接口,不是类型。
- 先检查文档或源码:看该类型是否带
Error()、String()或Message()方法;有就手动包装成fmt.Errorf - 常见做法是写一个轻量适配器函数:
func (e *LibError) Error() string { return e.Message },然后给结构体加这个方法(注意接收者是值还是指针) - 别用
fmt.Sprintf("%v", e)直接塞进errors.New——丢失原始类型信息,下游无法做类型断言
把 github.com/pkg/errors 错误转成标准 error 时丢堆栈怎么办
旧项目用了 pkg/errors 的 Wrap、WithStack,升级到 Go 1.13+ 后想用 errors.Is/errors.As,但发现转换后堆栈没了。
因为 pkg/errors 的堆栈是私有字段,标准 error 接口不暴露它;标准库的 fmt.Errorf 也不保留第三方堆栈。
立即学习“go语言免费学习笔记(深入)”;
- 如果必须保留堆栈,别转:继续用
pkg/errors的Cause和StackTrace,或迁移到golang.org/x/xerrors(已归档,但兼容性好) - 若只需兼容标准库判断,用
errors.Unwrap逐层剥开,再用errors.Is检查底层错误值,堆栈本身不参与判断 - 避免这种写法:
err = errors.New(pkgErr.Error())——彻底丢元数据,且errors.Is失效
调用 database/sql 时 driver.ErrBadConn 总被当成真实错误处理
这是典型适配不当:MySQL/PostgreSQL 驱动返回的 driver.ErrBadConn 是提示连接失效,应重试,不是业务错误。但它的类型是 error,又没导出,没法直接比较。
驱动作者故意不导出它,就是逼你用 errors.Is 做语义判断,而不是 ==。
- 正确方式:
if errors.Is(err, sql.ErrConnDone) || errors.Is(err, driver.ErrBadConn)——注意driver.ErrBadConn是变量,需导入database/sql/driver - 别用
strings.Contains(err.Error(), "bad connection"):字符串匹配脆弱,不同驱动报错文案不一致 - 某些驱动(如
pgx)自己封装了更明确的错误类型(如*pgconn.PgError),优先用errors.As断言具体类型
自定义错误类型在跨服务传递时丢失上下文
HTTP API 返回 JSON 错误时,如果直接序列化自定义 error 结构体,前端看到的是空对象或 panic;gRPC 里没实现 GRPCStatus() 就无法映射状态码。
错误不是日志,不该靠结构体字段“传过去”;它得在边界处被翻译成对方能理解的格式。
- HTTP 层统一用
json.Marshal(map[string]string{"error": err.Error()}),别暴露内部字段;需要 code 就额外加"code": "db_timeout" - gRPC 服务中,所有返回错误必须过
status.Errorf或status.FromContextError,否则客户端收不到Code() - 别在错误里存指针、切片、函数——序列化会 panic 或泄漏内存










