What does vendor/modules.txt record, and why does a build fail when it disagrees with go.mod?
answer
- the copied files say nothing about versions
- something has to name what the tree is
- a marker distinguishes direct from indirect
- the check runs before anything compiles
- the error names the one command to run
basics
~20 svendor/modules.txt indexes the vendored tree: each module, its selected version, whether go.mod requires it directly, and the packages copied from it. In vendor mode the go command compares that index with go.mod and refuses to build when the two disagree.
solid answer
~50 s`vendor/modules.txt` is written by `go mod vendor` and is the only record of *what* the copied source actually is. For each module it holds a line with the module path and version, an annotation marking whether the main module requires it directly, and then one line per package copied. The vendored `.go` files carry no version information themselves, so the `go` command treats this file as the authority. In vendor mode it cross-checks the index against `go.mod` before compiling anything: every module `go.mod` requires must be marked explicit, recorded versions must match the selected ones, and nothing may be listed that `go.mod` no longer requires. When they diverge — almost always because someone edited `go.mod` and did not re-run `go mod vendor` — the build stops with `go: inconsistent vendoring`, lists each mismatch, and tells you to regenerate.
code
text · 4 lines# golang.org/x/text v0.14.0
## explicit
golang.org/x/text/transform
golang.org/x/text/unicode/normgo deeper
Know that the error message tells you the fix. When a build stops with inconsistent vendoring, the answer is to run go mod vendor and commit what changes, not to edit files under vendor by hand.
Be able to explain why the index file must exist at all — copied source records no version — and to name the three comparisons the check makes between it and go.mod.
Show that you know the check validates identity, not content: matching versions do not prove the bytes are genuine. Say what you would add to CI to cover the gap.
The judgment here is about which failures you want loud. A build that refuses to start on a stale index is a deliberate trade of convenience for the guarantee that a shipped artifact matches its declared dependency set.
## Why an index file has to exist Once `go mod vendor` has copied dependency source into `vendor/`, those files are indistinguishable from any other Go source. A file under `vendor/golang.org/x/text/unicode/norm/` does not say which module it belongs to, and it certainly does not say which version it was taken from. If the `go` command had nothing but the tree, it could not answer the most basic question about a vendored build: *which versions am I compiling?* `vendor/modules.txt` answers that. It is generated by `go mod vendor` and is not meant to be edited by hand. ## What is in it The file is a flat, line-oriented record. For each module that contributed packages, there is: - a `#` line with the **module path and selected version**; - a `##` annotation line, which marks the module as `explicit` when the main module's `go.mod` requires it directly (and, on recent toolchains, also records the go language version that module declares); - one plain line per **package** copied from that module. ``` # golang.org/x/text v0.14.0 ## explicit golang.org/x/text/transform golang.org/x/text/unicode/norm ``` The `explicit` marker is the interesting one. It is how the file distinguishes a module you require directly from one that arrived only because something else needed it, and it is the marker the consistency check leans on hardest. ## The consistency check In vendor mode the `go` command does not simply trust the tree. Before it compiles anything, it compares `modules.txt` with `go.mod` and requires all of the following: - every module **required in `go.mod`** is marked `explicit` in `modules.txt`; - every module marked `explicit` in `modules.txt` is **required in `go.mod`**; - the **version** recorded for a module matches the version `go.mod` selects. If any of these fail, the build stops before compiling: ``` go: inconsistent vendoring in /home/build/svc: golang.org/x/[email protected]: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt To ignore the vendor directory, use -mod=mod or -mod=readonly. To sync the vendor directory, run: go mod vendor ``` The error is unusually helpful: it names each offending module, says which side of the comparison it came from, and gives you both the escape hatch and the fix. ## Why the failure is a feature The check exists because the alternative is worse. Without it, editing `go.mod` — adding a dependency, bumping a version — and forgetting to regenerate would produce a build that succeeds while compiling *the old source* against *new requirements*. The binary would not match its own declared dependency set, and nothing in the repository would say so. You would find out from behaviour, not from tooling. By failing at the very start of the build, the `go` command guarantees that a vendored build either matches `go.mod` in module identity and version, or does not happen at all. ## Reading the error correctly The three shapes of mismatch map to three ordinary situations: - **Required in `go.mod`, not explicit in `modules.txt`** — a dependency was added and the tree was not regenerated. - **Explicit in `modules.txt`, not required in `go.mod`** — a dependency was removed (or demoted to indirect) and the tree was not regenerated. - **Version mismatch** — a version was bumped and the tree was not regenerated. All three have the same fix, and it is the one the error prints: ``` go mod vendor ``` Then commit everything it changed — both `vendor/modules.txt` and the source it moved. A partial commit that includes the index but not the files (or the reverse) is a common way to reintroduce the problem in a form the check will not catch, because the check compares the index to `go.mod`, never to the files sitting next to it. ## The escape hatch, and when it is wrong The error also suggests `-mod=mod` or `-mod=readonly`, which ignore `vendor/` entirely and resolve from the module cache. That is genuinely useful for a local experiment, or for isolating whether a failure is caused by the vendored tree. Using it in CI to get past a red build is a mistake: the artifact then no longer comes from the committed source, which is the only reason the directory was checked in. ## What the check does not do It validates **identity**, not **content**. It confirms that the tree claims to be the right modules at the right versions. It does not verify that the copied bytes are what those versions actually contain — vendored source is not re-hashed at build time. A modified file under `vendor/` passes this check without complaint, which is why teams that vendor add a separate regeneration check to CI.
- Which kinds of disagreement trigger the inconsistent-vendoring error?A module required in `go.mod` that is not marked explicit in `modules.txt`; a module marked explicit that `go.mod` no longer requires; and a recorded version differing from the selected one. Each mismatch is listed by module path in the error, so you can tell whether the drift came from an addition, a removal or a version bump.
- Someone commits a go.mod change but forgets vendor/. What does CI see?An immediate failure with `go: inconsistent vendoring`, before anything compiles, because `go.mod` now requires a module the index does not mark explicit. That loud failure is the point — the alternative would be a green build compiling old source against new requirements. The fix is `go mod vendor` and a commit of everything it changed.
- Does the check verify that the vendored files are unmodified?No. It compares module paths, versions and explicit markers between `modules.txt` and `go.mod`, and never looks at the source bytes. A hand-edited file under `vendor/` passes the check and compiles normally. Detecting that requires a separate step, typically regenerating the tree in CI and failing if anything changed.
saying these in an interview costs you the question
- Thinks the vendored source files carry version information
- Says the go command derives versions by hashing vendor/
- Believes go build regenerates vendor/ on a mismatch
- Treats inconsistent vendoring as a corrupt checkout
- Deletes or hand-edits modules.txt to silence the error