底层返回 `sql.ErrNoRows`,上层包了一层再返回,调用方用 `errors.Is(err, sql.ErrNoRows)` 判断却始终是 false,只能退回字符串比对,改一次文案就得跟着改判断。第一次遇到会往复杂处想,其实漏掉的多是个小前提。

grep -rn 'fmt.Errorf' --include='*.go' .
grep -rn '%v' --include='*.go' .
go vet ./...

用 %v 或 %s 拼接错误只会保留文本,原来的错误对象被丢掉,errors.Is 沿链回溯时找不到目标哨兵值,包装等于把类型信息抹平了,日志看着没问题,判断全失效。这一点先确认清楚,后面就不用反复试。

「错误包装 %w」相关的验证我通常做两遍,改完一次、隔天再看一次,确认不是临时生效。新版本先放一台机器上跑一天再全量,问题一般在这段时间暴露。

备份不是备了就行,恢复一次才算数,很多备份文件打开才发现是空的。

具体做法是,把拼接处的格式化占位符换成可以回溯错误的写法;只在最外层补充上下文中间层不要重复包装;用类型判断函数替代原来那种脆弱的字符串匹配。

改完照着核一遍,错误判断恢复为真,日志里的错误链能看到两层上下文,不再断开。

作者 9823189