errors.Is常返回false,因其仅检查错误链中是否存在同一底层错误值或指针,不支持字符串匹配;须用%w包裹、预定义变量或自定义Unwrap方法才能正确识别。

errors.Is 为什么经常返回 false?
因为 errors.Is 只检查错误链中是否存在某个**底层错误值(value)或其指针**,不支持字符串匹配、模糊比较或自定义逻辑。如果你用 errors.New("timeout") 创建的错误和另一个同内容的 errors.New("timeout") 比较,errors.Is 一定返回 false —— 它们是两个不同地址的指针。
必须用 errors.New 或 fmt.Errorf 包装原始错误才能用 errors.Is
只有当错误被显式地用 fmt.Errorf("...: %w", err) 或 errors.Join 等方式包裹(即包含 %w 动词),它才进入错误链;否则 errors.Is 查不到。
- ✅ 正确:用
%w包裹底层错误 - ❌ 错误:只用
%s或拼接字符串,如fmt.Errorf("failed: %s", err.Error()) - ⚠️ 注意:
errors.Unwrap只能解一层,而errors.Is会自动遍历整个链(包括嵌套的%w)
err := io.EOF
wrapped := fmt.Errorf("read failed: %w", err)
fmt.Println(errors.Is(wrapped, io.EOF)) // true
自定义错误类型要实现 Unwrap 方法
如果自己定义了错误结构体(比如 type MyError struct{ Msg string; Code int }),默认 errors.Is 查不到它是否等于某个值,除非你显式实现 Unwrap() error 并返回底层错误。
1.修正会员卡升级会员级别的判定方式2.修正了订单换货状态用户管理中心订单不显示的问题3.完善后台积分设置数据格式验证方式4.优化前台分页程序5.解决综合模板找回密码提示错误问题6.优化商品支付模块程序7.重写优惠卷代码8.优惠卷使用方式改为1卡1号的方式9.优惠卷支持打印功能10.重新支付模块,所有支付方式支持自动对账11.去掉规格库存显示12.修正部分功能商品价格显示4个0的问题13.全新的支
- 若该错误本身是终端错误(无底层),
Unwrap应返回nil - 若它封装了另一个错误(如调用了
io.ReadFull),就返回那个错误 - 不要在
Unwrap中返回新构造的errors.New—— 地址不同,errors.Is仍失败
type MyError struct {
Err error
}
func (e *MyError) Error() string { return "my error" }
func (e *MyError) Unwrap() error { return e.Err } // ✅ 这样 errors.Is 才能穿透
判断标准错误时,优先用预定义变量而非 errors.New
Go 标准库导出的错误(如 io.EOF、os.ErrNotExist)是包级变量,地址唯一。直接拿它们做 errors.Is(err, io.EOF) 是安全且推荐的;但千万别写成 errors.Is(err, errors.New("EOF"))。
立即学习“go语言免费学习笔记(深入)”;
- ✅
errors.Is(err, io.EOF) - ✅
errors.Is(err, os.ErrNotExist) - ❌
errors.Is(err, errors.New("file does not exist")) - ⚠️ 第三方库若没暴露错误变量,应查文档看它是否实现了
Unwrap或提供了IsXXX()辅助函数
%w,导致上游再也无法用 errors.Is 判断原始错误类型。这个断点一旦出现,调试时就会发现“明明报了 EOF,却进不了 EOF 分支”。









