根本原因是$gopath/pkg/mod目录被设为只读,导致go mod download无法写入缓存;应执行go clean -modcache后chmod -r u+w $(go env gopath)/pkg/mod重置权限。

为什么 go mod download 报 “permission denied” 且提示无法读取模块目录
根本原因不是网络或代理问题,而是 Go 在 $GOPATH/pkg/mod 下缓存的模块目录被设为只读(例如由 go mod verify 或外部工具误改权限,或 macOS 上通过 Time Machine 恢复后继承了写保护位)。Go 1.16+ 默认启用 GOSUMDB=off 或校验失败时不会自动跳过,但更常见的是:它尝试重写一个已被 chmod -w 锁死的目录 —— 此时错误常表现为:open /path/to/pkg/mod/cache/download/...: permission denied。
- 典型触发场景:在 CI 环境中用非 root 用户执行
go mod download前手动清空并chown错误、或本地开发中用sudo go mod混用导致权限混乱 - 注意:这不是
GO111MODULE=off的问题,也不是go.sum冲突 —— 那些报错会明确含 “checksum mismatch” 或 “no required module” - 验证方式:运行
ls -ld $(go env GOPATH)/pkg/mod,若输出含dr-xr-xr-x(即无 w 权限),就是它
直接修复:重置 pkg/mod 目录权限并清理缓存
不要试图单个修复子目录 —— Go 的模块缓存是树状结构,写保护可能已扩散到多层。最稳妥的做法是彻底清理 + 重置根目录权限:
- 执行
go clean -modcache(它会删掉整个pkg/mod目录,但不改权限) - 紧接着运行
chmod -R u+w $(go env GOPATH)/pkg/mod(确保用户有写权限,-R必须加,因为go clean后目录重建前可能残留只读父目录) - 再跑一次
go mod download,此时 Go 会重新拉取并写入,不再卡在 open 失败 - 如果仍失败,检查
$(go env GOPATH)是否指向 NFS 或加密卷(某些挂载默认禁用 write)—— 这类环境需显式加rw选项重新挂载
避免复发:CI 和 Docker 中的权限惯性陷阱
本地修好不等于部署环境安全。很多 CI 脚本用 chown -R 1001:1001 /home/user/go 却漏掉 pkg/mod 子目录的 sticky bit 或 umask 影响,导致新建目录沿用只读掩码。
- Dockerfile 中别用
USER 1001后直接RUN go mod download—— 改为先RUN mkdir -p /home/user/go/pkg/mod && chmod -R u+w /home/user/go/pkg/mod - GitHub Actions 中,若用
actions/setup-go,默认不清理旧缓存;建议加步骤:run: rm -rf $HOME/go/pkg/mod(配合后续go mod download) - 关键点:Go 不会在每次
go mod前主动检查目录可写性,它只在真正写入时 panic —— 所以测试必须包含真实下载动作,不能只跑go list -m all
替代方案:临时绕过写保护(仅调试用)
当无法修改文件系统权限(如受限容器、共享构建机),可用 GOPATH 重定向隔离风险:
- 设置临时 GOPATH:
GOPATH=$(mktemp -d) go mod download,这样所有模块缓存写入新目录,完全避开旧路径 - 注意:这会导致后续
go build找不到已下载模块,除非全程用同一GOPATH;适合单次 CI job,不适合本地开发流 - 不推荐设
GOMODCACHE单独指向只读挂载 —— Go 仍会尝试在该路径下创建子目录,同样触发 permission denied
真正的麻烦不在第一次出错,而在你修完权限却忘了 CI 配置里那行残留的 chmod -R 444 —— 它安静地等下次构建时再咬你一口。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











