Go中跨goroutine错误传递必须显式发送error值,推荐用errgroup.Group统一收集,其次可用带缓冲chan error配合defer close,或errors.Join合并多错误。

Go 里跨 goroutine 错误传递不能靠 panic 冒泡,必须显式发送 error 值——否则错误就丢了,程序照常跑,但结果错、日志空、排查无从下手。
用带缓冲的 chan error 接收单个 goroutine 错误
最轻量也最容易出错的方式:为每个 goroutine 配一个 chan error,结束时发一次(哪怕 nil)。
- 必须用带缓冲 channel,比如
make(chan error, 1);无缓冲 channel 在主 goroutine 还没开始接收时,子 goroutine 发送就会永久阻塞 - 务必在 goroutine 结束前发一次 error,否则主 goroutine
<-errCh会卡死 - 别在 goroutine 外层 recover —— 它只对当前 goroutine 生效,放错位置等于没写
- 建议搭配
defer close(errCh),避免主 goroutine 因 channel 未关闭而无法退出 range
多个 goroutine 并发时用 errgroup.Group 统一收集
标准库 golang.org/x/sync/errgroup 是专为此设计的:它自动处理 sync.WaitGroup、context.Context 和 panic 转 error,比手写 channel 更可靠。
- 默认行为是“任一错误即终止”,适合强一致性场景(如初始化阶段)
- 调用
g.Wait()后,返回第一个非nil错误;若要汇总全部错误,需自行收集或改用errors.Join - 内部已封装了 defer+recover,子 goroutine panic 会被转成
error并聚合,不用手动写 recover - 注意:传入的函数签名必须是
func() error,不能直接传带参数的闭包——要用i := i捕获循环变量
用 errors.Join 合并多个独立错误
当你要等所有 goroutine 都跑完,再把所有错误一起返回(比如批量写入多个服务),errors.Join 是 Go 1.20+ 的标准解法。
立即学习“go语言免费学习笔记(深入)”;
- 不要自己拼字符串或用切片拼 error,
errors.Join返回的 error 支持errors.Unwrap拆解,也兼容errors.Is/errors.As - 每个子 goroutine 应该把 error 包装上上下文,比如
fmt.Errorf("write to %s failed: %w", svc, err),再丢进切片 - 切片容量要预估好,避免频繁扩容;收集完记得清空或重置,防止复用时混入旧错误
- 注意
errors.Join不会自动触发 cancel,如需超时控制,仍要配合context.WithTimeout
真正容易被忽略的不是“怎么传”,而是“谁负责关 channel、谁负责 recover、谁决定重试”。一个没 close 的 chan error,一个漏掉的 defer,一个没检查的 err != nil,都可能让错误静默消失。工程上最稳的做法,是统一用 errgroup.Group 起头,再按需叠加 errors.Wrap 和 context 控制流。


















