go mod download -json 输出是每行一个 json 对象的流式格式,需逐行解析;error 非空才表示失败;不能仅凭它判断依赖齐全,因不检查 transitive 依赖、go.sum 或 build 约束;应结合 go list -m -json all 获取完整模块图。

go mod download -json 输出结构怎么看
它输出的是 JSON 数组,每行一个 struct,不是完整 JSON 文档——直接用 jq 或 json.Unmarshal 会报错。常见错误是写 json.Decode(os.Stdin) 却没意识到要逐行解析。
- 每一行对应一个模块下载事件,含
Path、Version、Error(失败时非空)、Info(.info 文件路径)、GoMod(mod 文件路径)等字段 -
Error字段存在且非空,才表示该模块下载失败;空字符串或缺失 ≠ 成功,得看有没有输出这一行 - 成功时
Info和GoMod是绝对路径,但依赖未缓存时可能为空(比如被replace覆盖或指向本地目录)
用 Go 脚本安全读取 go mod download -json 流
别用 exec.Command("go", "mod", "download", "-json").Output(),它会卡死——因为 go mod download -json 是流式输出,不等全部完成就持续写 stdout,而 .Output() 要等命令退出才读,但下载卡住时命令就不退出。
- 必须用
cmd.StdoutPipe()+ 实时扫描换行 +json.Decoder逐个解码 - 记得设超时:
cmd.Start()后用time.AfterFunc或context.WithTimeoutkill 进程,避免无限 hang - 捕获
cmd.Wait()错误判断是否因超时/信号中断;即使有失败模块,只要进程退出码是 0,说明“尽力而为”完成,错误已体现在 JSON 行里
decoder := json.NewDecoder(stdout)
for {
var m struct {
Path, Version, Error, Info, GoMod string
}
if err := decoder.Decode(&m); err != nil {
if err == io.EOF { break }
log.Fatal(err) // 解析某行 JSON 失败,不是 EOF 就真出错了
}
if m.Error != "" {
fmt.Printf("fail: %s@%s: %s\n", m.Path, m.Version, m.Error)
}
}
为什么不能只靠 go mod download -json 判断依赖是否齐全
它只反映「当前 go.mod 声明的直接依赖」是否能拉下来,不检查 transitive 依赖是否满足、不校验 go.sum、也不触发 build 约束(比如 //go:build)。常见误判场景:
- 模块被
replace指向本地路径,go mod download -json仍返回成功,但本地路径若删了就编译不过 - 某个间接依赖在
go.sum中哈希不匹配,download -json不报错,但后续go build会 halt 并提示checksum mismatch - 跨平台构建时(如 darwin/amd64 下跑命令),某些依赖的
go:buildtag 排除当前平台,download -json仍照常输出,但实际不会参与编译
替代方案:什么时候该用 go list -m -json all
如果目标是「获取完整模块图」而非「触发下载」,go list -m -json all 更可靠:它不下载,只解析 go.mod 和 vendor(如有),输出所有已知模块(含 indirect),字段更全(含 Indirect、Dir、GoMod 是否存在)。
立即学习“go语言免费学习笔记(深入)”;
- 没有网络请求,秒出结果;适合 CI 中快速枚举依赖树
- 不会因私有仓库鉴权失败而中断,所有模块都列出来,
Dir为空代表未缓存或不可达 - 注意:它不保证模块内容可用,只是声明层面的快照;想验证可访问性,还得配合
go mod download -json或实际go build -n
go mod download -json 是“我试着下,下到哪算哪”,go list -m -json all 是“我纸上谈兵列个表”。漏掉哪个环节,脚本就可能在真正 build 时才翻车。










