golang/go · error

module %s listed in go.work file requires go >= %s, but go.w

Error message

module %s listed in go.work file requires go >= %s, but go.work %s go %s; to download and use go %s:
	go work use

What it means

errWorkTooOld: a module listed in go.work requires a newer Go version than go.work itself declares. Produced when a member's go.mod go directive exceeds the go.work go version (and the member is >= GoStrictVersion). The message tells the user to run 'go work use' which will bump go.work's go directive after they obtain the newer toolchain.

Source

Thrown at src/cmd/go/internal/modload/init.go:1167

				}
			}
		} else {
			rawGoVersion.Store(mainModule, gover.DefaultGoModVersion)
		}
	}

	ld.requirements = rs
	return ld.requirements, nil
}

func errWorkTooOld(gomod string, wf *modfile.WorkFile, goVers string) error {
	verb := "lists"
	if wf == nil || wf.Go == nil {
		// A go.work file implicitly requires go1.18
		// even when it doesn't list any version.
		verb = "implicitly requires"
	}
	return fmt.Errorf("module %s listed in go.work file requires go >= %s, but go.work %s go %s; to download and use go %s:\n\tgo work use",
		base.ShortPath(filepath.Dir(gomod)), goVers, verb, gover.FromGoWork(wf), goVers)
}

// CheckReservedModulePath checks whether the module path is a reserved module path
// that can't be used for a user's module.
func CheckReservedModulePath(path string) error {
	if gover.IsToolchain(path) {
		return errors.New("module path is reserved")
	}

	return nil
}

// CreateModFile initializes a new module by creating a go.mod file.
//
// If modPath is empty, CreateModFile will attempt to infer the path from the
// directory location within GOPATH.
//

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Upgrade the toolchain to the required version (GOTOOLCHAIN=auto or go get toolchain@go1.X).
  2. Run 'go work use' as the message instructs; it bumps the go.work go directive to match.
  3. Manually edit go.work 'go X.Y' to >= the member's version.
  4. If the member's high directive is unintended, lower that member's go directive instead.

Example fix

// before
go.work:  go 1.21
svc-b/go.mod:  go 1.22
// module svc-b listed in go.work requires go >= 1.22, but go.work lists go 1.21

// after
$ GOTOOLCHAIN=auto go work use   // go.work bumped to 'go 1.22'
Defensive patterns

Strategy: validation

Validate before calling

// Ensure go.work go version >= every member's go directive.
data, _ := os.ReadFile("go.work")
wf, _ := modfile.ParseWork("go.work", data, nil)
wv := gover.FromGoWork(wf)
for _, u := range wf.Use {
    mdata, _ := os.ReadFile(filepath.Join(u.Path, "go.mod"))
    mf, _ := modfile.Parse("go.mod", mdata, nil)
    mv := gover.FromGoMod(mf)
    if gover.Compare(mv, wv) > 0 {
        return fmt.Errorf("bump go.work go to %s (member %s needs it)", mv, u.Path)
    }
}

Try / catch

out, err := exec.Command("go", "build", "./...").CombinedOutput()
if err != nil && bytes.Contains(out, []byte("requires go >=")) && bytes.Contains(out, "go.work") {
    // bump toolchain then 'go work use' to realign
    _ = os.Setenv("GOTOOLCHAIN", "auto")
    _ = exec.Command("go", "work", "use").Run()
    out, err = exec.Command("go", "build", "./...").CombinedOutput()
}
return err

Prevention

When it happens

Trigger: go.work says 'go 1.21' but a member go.mod says 'go 1.22'; both are >= GoStrictVersion so the comparison gover.Compare(mv, wv) > 0 triggers errWorkTooOld. Also fires via the TooNewError branch in 1069's neighborhood.

Common situations: Adding/upgrading a dependency submodule that bumped its go directive; merging a newer member into an older workspace; CI toolchain behind the member requirements.

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/6530871c4f873c7c. Report an issue: GitHub.