skip to content

After a dependency upgrade, go build fails saying a module requires go >= 1.24 while CI runs Go 1.22 — why, and what are your options?

level: seniorimportance: should knowfreq 38%

answer

  1. the floor is a maximum over modules
  2. your own go line may have moved too
  3. read the go.mod diff first
  4. list every module's recorded go line
  5. lowering your go line fixes nothing

basics

~20 s

A dependency you pulled in declares a go line of 1.24, and every module contributing packages to the build must be buildable by the release doing the build. The upgrade may also have raised your own go directive to match. Read the go.mod diff, then either move CI forward or step the dependency back.

solid answer

~50 s

The effective minimum for a build is the highest go line among the main module and every module supplying packages to it — a dependency can therefore raise your floor without you touching your own go directive. Check two things: `git diff go.mod`, because adding a requirement can also bump your own go line, and `go list -m -f '{{.Path}} {{.GoVersion}}' all` to see which module actually demands 1.24. Then choose. Moving the CI image to a supported Go release is usually the right answer, since a release pinned two years back is out of support anyway. If you cannot move yet, step the dependency back with `go get [email protected]` and confirm the resolved build list no longer contains a 1.24 module. Note that your own go line does not have to equal the dependency's — only the installed release must satisfy the maximum.

code

text · 1 line
text
go list -m -f '{{.Path}} {{.GoVersion}}' all

go deeper

for a junior

Know that the error is about the Go release performing the build, not about the dependency's own release number, and that go.mod records a go line for every module, not just yours.

for a middle

Explain that the effective requirement is the maximum go line across the main module and all modules supplying packages, and show how to list those values.

for a senior

Drive the diagnosis and pick between moving the CI image and stepping the dependency back, justifying the choice against the support window and the freeze you are actually in.

for a principal

Own the policy that prevents the fire drill: one place where the Go release is pinned, CI on the oldest supported release, go.mod diffs reviewed as compatibility events, and a published support window for your own consumers.

## Whose version requirement is this, exactly? The error names a module and a version, and the first job is to work out which module — because there are two independent ways a build acquires a floor of `go 1.24`. ### Source 1: a dependency's own go line Every module records its own go directive, and every module whose packages are compiled into your build has to be compilable by the release doing the build. So when a dependency's maintainer raises their go line to 1.24 and you upgrade to that version, your build now needs a Go 1.24 or newer install, even though your own go.mod is untouched. This is the mechanism people find surprising: you did not change a version, and yet your version floor moved. Importantly, this does **not** mean your module must declare `go 1.24` too. Language versions are per module: your files can keep compiling at 1.21 while the dependency's compile at 1.24. Only the installed release has to satisfy the maximum across the build. ### Source 2: your own go line, quietly raised The go command keeps go.mod consistent with what your requirements need, so an upgrade command that pulls in a module with a higher go line can rewrite your own go directive as part of the same operation. That change is in the diff, and reviewing the go.mod diff alongside the go.sum churn is the habit that turns this into a five-minute diagnosis instead of an afternoon. ### Diagnosing it 1. `git diff go.mod go.sum` — did the go directive move? Which requirement is new or upgraded? 2. `go list -m -f '{{.Path}} {{.GoVersion}}' all` — prints the recorded go line for every module in the build list, so the 1.24 offender is a scan away. 3. `go version` — confirm what the CI image is actually running, which is frequently not what the README claims. ### The options, in the order most teams should consider them **Move CI forward.** Usually correct. Go supports each major release until two newer ones have shipped, so pinning several releases back means running something that no longer receives fixes. A version floor arriving from the ecosystem is the ecosystem telling you your image is old. Change it in one place, run the suite, done. **Step the dependency back.** Legitimate when the image genuinely cannot move this week — a platform migration in flight, a certified base image, a release freeze. `go get [email protected]` selects the last version whose go line you can satisfy; re-run the build list check to confirm nothing else in the graph still demands 1.24, because a transitive requirement can hold the floor up on its own. Record why, with a date, or the pin becomes permanent by accident. **Drop or replace the dependency.** Worth considering when the dependency is small and the floor it imposes is disproportionate to what it does for you. **Lowering your own go line.** Almost always the wrong reflex, and it does not help: `go mod tidy -go=1.22` sets *your* language version, but it cannot lower the dependency's go line, so the build still needs a 1.24 install. This is the single most common wasted step on this failure. ### Making it a non-event next time - Pin the Go release in one place — the CI image definition — and treat raising it as a normal, scheduled change rather than an emergency. - Build the module on the oldest release you claim to support, in CI, on every change. Otherwise "we support Go 1.22" is a belief rather than a fact, and you discover it is false at the worst moment. - Review go.mod diffs like code. The go directive moving is a compatibility event for anyone who consumes your module, not a formatting change. - Publish the release you support so your own consumers can plan; a floor that moves without notice is the same problem one level down.

  • Does your main module have to declare `go 1.24` because a dependency does?
    No. Language versions are per module, so your files can keep compiling at their own, lower version while the dependency's compile at 1.24. What must satisfy the maximum is the Go release performing the build. In practice an upgrade command may still raise your go line to keep go.mod consistent, which is worth spotting in the diff.
  • Why does running `go mod tidy -go=1.22` not fix this failure?
    Because it changes your module's declared language version and nothing else. The dependency still records `go 1.24` in its own go.mod, and the build still needs a release that new. The only fixes are a newer Go install or a build list that no longer contains a module demanding 1.24.
  • You step the dependency back and the failure persists. What is the likely cause?
    Something else in the graph still requires a 1.24 module — often a transitive requirement pulled up by minimum version selection from another direct dependency. Re-run the build-list listing of recorded go lines after the downgrade rather than assuming the direct requirement was the only source.
  • How do you stop this from surprising you again?
    Run CI against the oldest Go release you claim to support, on every change, so the claim is tested rather than assumed. Keep the release pinned in exactly one place, review go.mod diffs as compatibility events, and schedule toolchain bumps rather than reacting to them.

saying these in an interview costs you the question

  • Tries to fix it by lowering the main module's go directive
  • Assumes the main module must match the dependency's go line
  • Never checks which module actually recorded the higher version
  • Pins the dependency permanently with no date or owner
  • Believes a dependency's go line cannot affect their build