go build 报“module is not a dependency”是因为 Go 工具链仅识别当前构建上下文(最近上级 go.mod)声明的依赖,不会自动发现同级或子目录中独立的 go.mod;解决需在引用方 go.mod 中显式 require + replace(开发期),上线前须移除 replace 并发布带语义化版本 tag 的子模块,同时确保 GOPRIVATE 配置、各模块独立维护 go.sum、且 module path 全局唯一稳定。

为什么 go build 会报 “module is not a dependency”
这不是模块没写对,而是 Go 工具链根本没把目标包识别为当前构建上下文的依赖。Go 默认只认当前目录向上最近的 go.mod,不会自动扫描同级或子目录下的其他 go.mod。比如你在 cmd/app 目录下执行 go build,而它想 import pkg/utils,但 pkg/utils/go.mod 的 module path 是 example.com/myproject/pkg/utils,且未在 cmd/app/go.mod 中声明 require,就会直接失败。
解决方式只有一条:在引用方的 go.mod 里显式 require + replace(开发阶段):
-
require example.com/myproject/pkg/utils v0.0.0—— 占位用,版本号不重要 -
replace example.com/myproject/pkg/utils => ../pkg/utils—— 路径必须是相对于当前go.mod文件的,不能用绝对路径 - 执行
go mod tidy后,go list -m all应该显示该模块后缀带=> ../pkg/utils
replace 指令在 CI 中失效怎么办
replace 是开发期临时方案,CI 环境默认禁用它(尤其当设置了 GOFLAGS="-mod=readonly")。构建失败时看到 cannot find module,基本就是 replace 还没删干净,或者子模块没发布到可访问的仓库。
上线前必须清理掉所有 replace 行,并确保:
立即学习“go语言免费学习笔记(深入)”;
- 子模块已打 tag(如
v1.2.0),且 tag 名符合语义化版本规则 - 主模块的
require行已更新为真实版本:require example.com/myproject/pkg/utils v1.2.0 - 私有模块需提前配置
GOPRIVATE=git.example.com/*,否则go mod download会走 proxy 并 404 - CI 构建前先跑一遍
cd ../pkg/utils && go mod tidy && cd -,避免子模块依赖不一致
go.sum 不同步导致 checksum mismatch
每个模块都维护自己的 go.sum,不是“根目录一份就够”。如果子模块更新了依赖但没提交新的 go.sum,主模块 go mod tidy 时不会自动拉取它的变更,结果就是校验和对不上。
关键动作不是删 go.sum,而是让每个模块自己“认领”依赖:
- 不要删除子模块的
go.sum;也不要手动合并多个go.sum - 在子模块目录下运行
go mod tidy→ 提交该模块的go.mod和go.sum - 主模块运行
go mod tidy时,会读取子模块最新 commit 的go.sum记录,生成对应校验和 - CI 构建前加一步
go mod verify,比等到go build报错更早发现问题
什么时候该拆出独立 module,而不是放 internal 或 pkg
拆 module 不是为了“看起来分层”,而是为了满足可复用、可独立发版这两个硬条件。比如 internal/service 永远不该是一个 module —— 它的代码对外不可见,也不需要单独 go get。
真正该拆的只有三类:
- 会被外部项目直接
import的库,比如pkg/logger、pkg/metrics - 需要独立 CI/CD 流程和版本号的服务,比如
service/user、service/order - 要作为 SDK 发布给第三方集成的组件,比如
client/sdk
最容易被忽略的是:module 名必须全局唯一且稳定。一旦发布,改名等于发布新模块,旧版本无法自动迁移。所以初始化时别图省事用 ./pkg/utils,而要用带域名的完整路径,比如 example.com/myproject/pkg/utils。


















