正确包装错误必须使用fmt.Errorf("%w"),自定义类型需显式实现Unwrap()方法;errors.Is和errors.As依赖完整错误链,任意环节用错动词或漏掉Unwrap都会导致匹配失效。

错误包装不是加个字符串就完事,用错动词或漏掉Unwrap实现,errors.Is和errors.As立刻失效。
fmt.Errorf("%w") 是唯一安全的包装入口
Go 1.13+ 的错误链依赖%w动词触发标准包装逻辑。用%v、%s或+拼接,哪怕内容一模一样,返回的都是*fmt.errorString,Unwrap()返回nil,整条链就此断裂。
-
fmt.Errorf("read config: %w", err)✅ 支持errors.Is穿透 -
fmt.Errorf("read config: %v", err)❌errors.Is(err, fs.ErrNotExist)永远返回false -
"read config: " + err.Error()❌ 不是error类型,连errors.Unwrap都调不了 - Go 1.22 起支持多个
%w,但需显式标注:fmt.Errorf("a: %w, b: %w", errA, errB),旧版本仍只认一个
errors.Unwrap 只解一层,别指望它自动递归
errors.Unwrap设计上就是单步操作——它不判断环、不跳过nil、不递归向下。你得自己控制循环和终止条件。
- 裸调
errors.Unwrap(err)后不检查返回值,可能对nil再调一次导致 panic - 正确写法是:
for err != nil { ...; err = errors.Unwrap(err) } - 绝大多数场景根本不需要手动
Unwrap:用errors.Is(err, target)或errors.As(err, &target)更安全,它们内置环检测和完整遍历 - 自定义错误类型若未实现
Unwrap() error,errors.Unwrap直接返回nil,不会尝试反射或 fallback
自定义错误必须显式实现 Unwrap 才能接入链
结构体错误默认是“黑盒”,errors.Is只能匹配它自身,无法穿透到内部字段。除非你告诉运行时:“我包裹了这个错误”。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
立即学习“go语言免费学习笔记(深入)”;
- 漏掉
Unwrap方法是最常见失误,表面fmt.Errorf("xxx: %w", myErr)能编译,但上游errors.Is全失效 - 实现要点:返回字段中保存的
error值,不能返回nil(除非确实无底层错误),也不能返回硬编码错误(如io.ErrUnexpectedEOF) - 示例:
type ValidationError struct { Msg string Code int Cause error // 必须存为 error 类型 } func (e *ValidationError) Unwrap() error { return e.Cause } - 如果
Cause本身是nil,Unwrap返回nil合法,errors.Is会继续匹配*ValidationError自身
errors.Is 和 errors.As 的行为边界很窄
这两个函数只认实现了Unwrap() error的错误节点,且严格按链式顺序逐层调用。任何一层返回nil或非error值,遍历就停在那里。
-
errors.Is(err, io.EOF)成功,前提是链上每一层都用%w包装,且没有中间层返回nil的Unwrap() -
errors.As(err, &pe)要求pe是指针(如*os.PathError),传值类型或非指针会静默失败 - 哨兵错误(如
var ErrNotFound = errors.New("not found"))必须导出且全局唯一;用errors.New("not found")新建一个,errors.Is就认不出 - 日志里别用
err.Error()做判断——字符串匹配脆弱,且丢失所有类型信息和链结构
真正难的不是写对第一层%w,而是确保整个调用链上每个参与包装的错误类型都守规矩:不用%v、不绕过Unwrap、不把哨兵当字符串重造。一旦某处松懈,下游所有Is/As逻辑就变成盲人摸象。

















