必须用%w包装且错误链完整,否则errors.is返回false;若中间层用%v、%s或errors.new,或底层错误未实现unwrap(),链即断裂。

Go 里的错误链不是“加个 %w 就完事”,而是要让每一层都回答三个问题:发生了什么、在哪里发生的、为什么发生。否则线上报个 internal error,你得 grep 五次才能找到 Redis 连不上那行。
用 fmt.Errorf("%w", err) 包装错误时,为什么 errors.Is 还是返回 false?
常见现象:明明写了 fmt.Errorf("save user failed: %w", err),但上层用 errors.Is(err, redis.ErrNil) 却不匹配。
- 检查是否用了
%v或%s替代%w—— 只有%w才保留原始错误引用,%v只是字符串拼接 - 确认底层错误是否本身支持包装:比如第三方库返回的错误没实现
Unwrap()方法,%w就无效 -
errors.Is是递归调用Unwrap()查找目标错误,如果中间某层用了errors.New或fmt.Errorf(无%w)就断链了
errors.As 提取自定义错误类型失败,该怎么排查?
典型场景:想从错误链里取出 *redis.RedisError 或自定义的 *AppError,但 errors.As(err, &target) 总是 false。
- 确保目标变量是指针类型,且类型实现了
error接口;errors.As不会自动解引用 - 检查包装链中是否有某一层把错误转成了字符串(比如
fmt.Sprintf("err: %v", err)),这会丢掉原始类型 - 如果底层错误是哨兵错误(如
var ErrTimeout = errors.New("timeout")),errors.As没法提取,只能用errors.Is
打印错误链时,%+v 输出一堆 unavailable 或空堆栈?
现象:用 fmt.Printf("%+v", err) 看调用链,却只看到 failed to save session: %!w(error=connection refused),没有文件名和行号。
- Go 默认不记录堆栈,
%+v能显示堆栈的前提是错误类型实现了fmt.Formatter接口并主动捕获位置信息 - 标准库错误(
errors.New、fmt.Errorf)不带堆栈;要用github.com/pkg/errors或 Go 1.22+ 的errors.Join+ 自定义错误类型才可能带 - 生产环境建议用结构化日志(如
zerolog)单独记录file:line,别依赖错误值本身存堆栈
真正难的不是写 %w,而是每层都得想清楚:这个错误对调用方意味着什么、要不要暴露细节、要不要允许被 Is 或 As 判断——漏掉任何一个,链就断在那一层了。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











