golangci-lint 默认不检查 error wrapping 问题,因 errcheck 设计目标仅为检测未处理的 error 返回值,需显式启用并配置 check-exported: true 和 exclude-functions 列表(如 t.Fatal、log.Fatal 等)才能识别 fmt.Errorf 中 %w 漏用或误用 %v 等关键缺陷。

golangci-lint 默认不检查错误包装(error wrapping)问题,必须显式启用 errcheck 并配置其 check-exported 和 exclude-functions,否则像 fmt.Errorf("xxx: %w", err) 漏掉 %w、或误用 %v 替代 %w 这类关键缺陷会被完全忽略。
为什么默认不报 error wrapping 错误
errcheck 的设计目标是“未处理的 error”,不是“包装不规范的 error”。它默认只检查函数返回值是否被忽略,而对 fmt.Errorf 内部格式动词是否含 %w 是不感知的——除非你开启它的高级检查模式。很多团队跑完 golangci-lint run 却没发现 fmt.Errorf("failed: %v", err) 这种静默丢失堆栈的问题,根源就在这儿。
-
errcheck默认仅检查调用后是否丢弃返回值(如os.Open("x")不赋值也不 _),不分析字符串格式 - 要检查
%w使用,必须设check-exported: true(否则连导出函数里的fmt.Errorf都不看) - 即使开了
check-exported,errcheck仍会把t.Fatal、log.Fatal等测试/日志函数当作“已处理”,从而跳过其内部fmt.Errorf——这需要额外排除
启用 error wrapping 检查的最小有效配置
在 .golangci.yml 的 linters-settings 下配 errcheck,且必须和 linters.enable 中启用它联动:
linters-settings:
errcheck:
check-exported: true
exclude-functions:
- "log.Fatal"
- "log.Fatalf"
- "t.Fatal"
- "t.Fatalf"
- "require.NoError"
- "assert.NoError"
linters:
enable:
- errcheck-
check-exported: true是开关,不开它,所有fmt.Errorf调用都不进检查逻辑 -
exclude-functions列表不是“忽略这些函数”,而是“把这些函数当成交互式终点,其参数里的 error 视为已处理”;漏写会导致大量误报(比如t.Fatalf("xxx: %v", err)被当成未包装) - 别写
exclude: ["log.*"]这类正则——errcheck的exclude-functions只支持全名精确匹配
常见误报与绕过方式
即便配置正确,errcheck 对 wrapping 的判断仍有局限,容易在以下场景报错:
立即学习“go语言免费学习笔记(深入)”;
- 使用了自定义错误构造函数,如
errors.Newf("xxx: %w", err):它只认fmt.Errorf,需加到exclude-functions或改用原生函数 - 包装发生在多层嵌套里,如
fmt.Errorf("outer: %w", fmt.Errorf("inner: %v", err)):第二层%v会被捕获,但工具无法推断外层是否补救,建议直接修复 - 第三方库返回的 error 被包装时路径不清晰(如
github.com/pkg/errors.Wrap):errcheck不识别,需禁用该 linter 或换用goerr113(非内置,需手动安装)
如果项目重度依赖 pkg/errors 或 github.com/ztrue/truth 等包装库,errcheck 就不是最佳选择——这时候应考虑 staticcheck 的 SA1029(检查 fmt.Errorf 是否用了 %w)并关闭 errcheck 的 wrapping 相关行为。
CI 中失效的典型原因
本地能报 wrapping 问题,CI 却安静通过,往往卡在三个地方:
- CI 命令没传
--config=.golangci.yml:自动发现机制在容器环境常失效,errcheck回退到默认(check-exported: false) - Go 版本低于 1.13:
%w语法虽能编译,但errcheck在旧版本中解析 format string 有兼容性 bug,建议 CI 锁定go: "1.21" - 配置文件缩进错误导致整个
linters-settings.errcheck块被跳过:YAML 里多一个空格,errcheck就不会读取任何子项,静默降级
真正难调试的是第三种——它不报错、不警告,只是让所有 wrapping 检查彻底消失。每次改完 .golangci.yml,务必用 yamllint .golangci.yml 扫一遍,再跑 golangci-lint run --debug 确认 errcheck 的实际生效配置。


















