Go Toolchains

Introduction

Starting in Go 1.21, the Go distribution consists of a go command and a bundled Go toolchain, which is the standard library as well as the compiler, assembler, and other tools. The go command can use its bundled Go toolchain as well as other versions that it finds in the local PATH or downloads as needed.

The choice of Go toolchain being used depends on the GOTOOLCHAIN environment setting and the go and toolchain lines in the main module’s go.mod file or the current workspace’s go.work file. As you move between different main modules and workspaces, the toolchain version being used can vary, just as module dependency versions do.

In the standard configuration, the go command uses its own bundled toolchain when that toolchain is at least as new as the go or toolchain lines in the main module or workspace. For example, when using the go command bundled with Go 1.21.3 in a main module that says go 1.21.0, the go command uses Go 1.21.3. When the go or toolchain line is newer than the bundled toolchain, the go command runs the newer toolchain instead. For example, when using the go command bundled with Go 1.21.3 in a main module that says go 1.21.9, the go command finds and runs Go 1.21.9 instead. It first looks in the PATH for a program named go1.21.9 and otherwise downloads and caches a copy of the Go 1.21.9 toolchain. This automatic toolchain switching can be disabled, but in that case, for more precise forwards compatibility, the go command will refuse to run in a main module or workspace in which the go line requires a newer version of Go. That is, the go line sets the minimum required Go version necessary to use a module or workspace.

Modules that are dependencies of other modules may need to set a minimum Go version requirement lower than the preferred toolchain to use when working in that module directly. In this case, the toolchain line in go.mod or go.work sets a preferred toolchain that takes precedence over the go line when the go command is deciding which toolchain to use.

The go and toolchain lines can be thought of as specifying the version requirements for the module’s dependency on the Go toolchain itself, just as the require lines in go.mod specify the version requirements for dependencies on other modules. The go get command manages the Go toolchain dependency just as it manages dependencies on other modules. For example, go get go@latest updates the module to require the latest released Go toolchain.

The GOTOOLCHAIN environment setting can force a specific Go version, overriding the go and toolchain lines. For example, to test a package with Go 1.21rc3:

GOTOOLCHAIN=go1.21rc3 go test

The default GOTOOLCHAIN setting is auto, which enables the toolchain switching described earlier. The alternate form <name>+auto sets the default toolchain to use before deciding whether to switch further. For example GOTOOLCHAIN=go1.21.3+auto directs the go command to begin its decision with a default of using Go 1.21.3 but still use a newer toolchain if directed by go and toolchain lines. Because the default GOTOOLCHAIN setting can be changed with go env -w, if you have Go 1.21.0 or later installed, then

go env -w GOTOOLCHAIN=go1.21.3+auto

is equivalent to replacing your Go 1.21.0 installation with Go 1.21.3.

The rest of this document explains how Go toolchains are versioned, chosen, and managed in more detail.

Go versions

Released versions of Go use the version syntax ‘1.N.P’, denoting the Pth release of Go 1.N. The initial release is 1.N.0, like in ‘1.21.0’. Later releases like 1.N.9 are often referred to as patch releases.

Go 1.N release candidates, which are issued before 1.N.0, use the version syntax ‘1.NrcR’. The first release candidate for Go 1.N has version 1.Nrc1, like in 1.23rc1.

The syntax ‘1.N’ is called a “language version”. It denotes the overall family of Go releases implementing that version of the Go language and standard library.

The language version for a Go version is the result of truncating everything after the N: 1.21, 1.21rc2, and 1.21.3 all implement language version 1.21.

Released Go toolchains such as Go 1.21.0 and Go 1.21rc1 report that specific version (for example, go1.21.0 or go1.21rc1) from go version and runtime.Version. Unreleased (still in development) Go toolchains built from the Go development repository instead report only the language version (for example, go1.21).

Any two Go versions can be compared to decide whether one is less than, greater than, or equal to the other. If the language versions are different, that decides the comparison: 1.21.9 < 1.22. Within a language version, the ordering from least to greatest is: the language version itself, then release candidates ordered by R, then releases ordered by P.

For example, 1.21 < 1.21rc1 < 1.21rc2 < 1.21.0 < 1.21.1 < 1.21.2.

Before Go 1.21, the initial release of a Go toolchain was version 1.N, not 1.N.0, so for N < 21, the ordering is adjusted to place 1.N after the release candidates.

For example, 1.20rc1 < 1.20rc2 < 1.20rc3 < 1.20 < 1.20.1.

Earlier versions of Go had beta releases, with versions like 1.18beta2. Beta releases are placed immediately before release candidates in the version ordering.

For example, 1.18beta1 < 1.18beta2 < 1.18rc1 < 1.18 < 1.18.1.

Go toolchain names

The standard Go toolchains are named goV where V is a Go version denoting a beta release, release candidate, or release. For example, go1.21rc1 and go1.21.0 are toolchain names; go1.21 and go1.22 are not (the initial releases are go1.21.0 and go1.22.0), but go1.20 and go1.19 are.

Non-standard toolchains use names of the form goV-suffix for any suffix.

Toolchains are compared by comparing the version V embedded in the name (dropping the initial go and discarding off any suffix beginning with -). For example, go1.21.0 and go1.21.0-custom compare equal for ordering purposes.

Module and workspace configuration

Go modules and workspaces specify version-related configuration in their go.mod or go.work files.

The go line declares the minimum required Go version for using the module or workspace. For compatibility reasons, if the go line is omitted from a go.mod file, the module is considered to have an implicit go 1.16 line, and if the go line is omitted from a go.work file, the workspace is considered to have an implicit go 1.18 line.

The toolchain line declares a suggested toolchain to use with the module or workspace. As described in “Go toolchain selection” below, the go command may run this specific toolchain when operating in that module or workspace if the default toolchain’s version is less than the suggested toolchain’s version. If the toolchain line is omitted, the module or workspace is considered to have an implicit toolchain goV line, where V is the Go version from the go line.

For example, a go.mod that says go 1.21.0 with no toolchain line is interpreted as if it had a toolchain go1.21.0 line.

The Go toolchain refuses to load a module or workspace that declares a minimum required Go version greater than the toolchain’s own version.

For example, Go 1.21.2 will refuse to load a module or workspace with a go 1.21.3 or go 1.22 line.

A module’s go line must declare a version greater than or equal to the go version declared by each of the modules listed in require statements. A workspace’s go line must declare a version greater than or equal to the go version declared by each of the modules listed in use statements.

For example, if module M requires a dependency D with a go.mod that declares go 1.22.0, then M’s go.mod cannot say go 1.21.3.

The go line for each module sets the language version the compiler enforces when compiling packages in that module. The language version can be changed on a per-file basis by using a build constraint: if a build constraint is present and implies a minimum version of at least go1.21, the language version used when compiling that file will be that minimum version.

For example, a module containing code that uses the Go 1.21 language version should have a go.mod file with a go line such as go 1.21 or go 1.21.3. If a specific source file should be compiled only when using a newer Go toolchain, adding //go:build go1.22 to that source file both ensures that only Go 1.22 and newer toolchains will compile the file and also changes the language version in that file to Go 1.22.

The go and toolchain lines are most conveniently and safely modified by using go get; see the section dedicated to go get below.

Before Go 1.21, Go toolchains treated the go line as an advisory requirement: if builds succeeded the toolchain assumed everything worked, and if not it printed a note about the potential version mismatch. Go 1.21 changed the go line to be a mandatory requirement instead. This behavior is partly backported to earlier language versions: Go 1.19 releases starting at Go 1.19.13 and Go 1.20 releases starting at Go 1.20.8, refuse to load workspaces or modules declaring version Go 1.22 or later.

Before Go 1.21, toolchains did not require a module or workspace to have a go line greater than or equal to the go version required by each of its dependency modules.

The GOTOOLCHAIN setting

The go command selects the Go toolchain to use based on the GOTOOLCHAIN setting. To find the GOTOOLCHAIN setting, the go command uses the standard rules for any Go environment setting:

In standard Go toolchains, the $GOROOT/go.env file sets the default GOTOOLCHAIN=auto, but repackaged Go toolchains may change this value.

If the $GOROOT/go.env file is missing or does not set a default, the go command assumes GOTOOLCHAIN=local.

Running go env GOTOOLCHAIN prints the GOTOOLCHAIN setting.

Go toolchain selection

At startup, the go command selects which Go toolchain to use. It consults the GOTOOLCHAIN setting, which takes the form <name>, <name>+auto, or <name>+path. GOTOOLCHAIN=auto is shorthand for GOTOOLCHAIN=local+auto; similarly, GOTOOLCHAIN=path is shorthand for GOTOOLCHAIN=local+path. The <name> sets the default Go toolchain: local indicates the bundled Go toolchain (the one that shipped with the go command being run), and otherwise <name> must be a specific Go toolchain name, such as go1.21.0. The go command prefers to run the default Go toolchain. As noted above, starting in Go 1.21, Go toolchains refuse to run in workspaces or modules that require newer Go versions. Instead, they report an error and exit.

When GOTOOLCHAIN is set to local, the go command always runs the bundled Go toolchain.

When GOTOOLCHAIN is set to <name> (for example, GOTOOLCHAIN=go1.21.0), the go command always runs that specific Go toolchain. If a binary with that name is found in the system PATH, the go command uses it. Otherwise the go command uses a Go toolchain it downloads and verifies.

When GOTOOLCHAIN is set to <name>+auto or <name>+path (or the shorthands auto or path), the go command selects and runs a newer Go version as needed. Specifically, it consults the toolchain and go lines in the current workspace’s go.work file or, when there is no workspace, the main module’s go.mod file. If the go.work or go.mod file has a toolchain <tname> line and <tname> is newer than the default Go toolchain, then the go command runs <tname> instead. If the file has a toolchain default line, then the go command runs the default Go toolchain, disabling any attempt at updating beyond <name>. Otherwise, if the file has a go <version> line and <version> is newer than the default Go toolchain, then the go command runs go<version> instead.

To run a toolchain other than the bundled Go toolchain, the go command searches the process’s executable path ($PATH on Unix and Plan 9, %PATH% on Windows) for a program with the given name (for example, go1.21.3) and runs that program. If no such program is found, the go command downloads and runs the specified Go toolchain. Using the GOTOOLCHAIN form <name>+path disables the download fallback, causing the go command to stop after searching the executable path.

Running go version prints the selected Go toolchain’s version (by running the selected toolchain’s implementation of go version).

Running GOTOOLCHAIN=local go version prints the bundled Go toolchain’s version.

Starting in Go 1.24, you can trace the go command’s toolchain selection process by adding toolchaintrace=1 to the GODEBUG environment variable when you run the go command.

Go toolchain switches

For most commands, the workspace’s go.work or the main module’s go.mod will have a go line that is at least as new as the go line in any module dependency, due to the version ordering configuration requirements. In this case, the startup toolchain selection runs a new enough Go toolchain to complete the command.

Some commands incorporate new module versions as part of their operation: go get adds new module dependencies to the main module; go work use adds new local modules to the workspace; go work sync resynchronizes a workspace with local modules that may have been updated since the workspace was created; go install package@version and go run package@version effectively run in an empty main module and add package@version as a new dependency. All these commands may encounter a module with a go.mod go line requiring a newer Go version than the currently executed Go version.

When a command encounters a module requiring a newer Go version and GOTOOLCHAIN permits running different toolchains (it is one of the auto or path forms), the go command chooses and switches to an appropriate newer toolchain to continue executing the current command.

Any time the go command switches toolchains after startup toolchain selection, it prints a message explaining why. For example:

go: module example.com/widget@v1.2.3 requires go >= 1.24rc1; switching to go 1.27.9

As shown in the example, the go command may switch to a toolchain newer than the discovered requirement. In general the go command aims to switch to a supported Go toolchain.

To choose the toolchain, the go command first obtains a list of available toolchains. For the auto form, the go command downloads a list of available toolchains. For the path form, the go command scans the PATH for any executables named for valid toolchains and uses a list of all the toolchains it finds. Using that list of toolchains, the go command identifies up to three candidates:

These are the supported Go releases according to Go’s release policy. Consistent with minimal version selection, the go command then conservatively uses the candidate with the minimum (oldest) version that satisfies the new requirement.

For example, suppose example.com/widget@v1.2.3 requires Go 1.24rc1 or later. The go command obtains the list of available toolchains and finds that the latest patch releases of the two most recent Go toolchains are Go 1.28.3 and Go 1.27.9, and the release candidate Go 1.29rc2 is also available. In this situation, the go command will choose Go 1.27.9. If widget had required Go 1.28 or later, the go command would choose Go 1.28.3, because Go 1.27.9 is too old. If widget had required Go 1.29 or later, the go command would choose Go 1.29rc2, because both Go 1.27.9 and Go 1.28.3 are too old.

Commands that incorporate new module versions that require new Go versions write the new minimum go version requirement to the current workspace’s go.work file or the main module’s go.mod file, updating the go line. For repeatability, any command that updates the go line also updates the toolchain line to record its own toolchain name. The next time the go command runs in that workspace or module, it will use that updated toolchain line during toolchain selection.

For example, go get example.com/widget@v1.2.3 may print a switching notice like above and switch to Go 1.27.9. Go 1.27.9 will complete the go get and update the toolchain line to say toolchain go1.27.9. The next go command run in that module or workspace will select go1.27.9 during startup and will not print any switching message.

In general, if any go command is run twice, if the first prints a switching message, the second will not, because the first also updated go.work or go.mod to select the right toolchain at startup. The exception is the go install package@version and go run package@version forms, which run in no workspace or main module and cannot write a toolchain line. They print a switching message every time they need to switch to a newer toolchain.

Downloading toolchains

When using GOTOOLCHAIN=auto or GOTOOLCHAIN=<name>+auto, the Go command downloads newer toolchains as needed. These toolchains are packaged as special modules with module path golang.org/toolchain and version v0.0.1-goVERSION.GOOS-GOARCH. Toolchains are downloaded like any other module, meaning that toolchain downloads can be proxied by setting GOPROXY and have their checksums checked by the Go checksum database. Because the specific toolchain used depends on the system’s own default toolchain as well as the local operating system and architecture (GOOS and GOARCH), it is not practical to write toolchain module checksums to go.sum. Instead, toolchain downloads fail for lack of verification if GOSUMDB=off. GOPRIVATE and GONOSUMDB patterns do not apply to the toolchain downloads.

Managing Go version module requirements with go get

In general the go command treats the go and toolchain lines as declaring versioned toolchain dependencies of the main module. The go get command can manage these lines just as it manages the require lines that specify versioned module dependencies.

For example, go get go@1.22.1 toolchain@1.24rc1 changes the main module’s go.mod file to read go 1.22.1 and toolchain go1.24rc1.

The go command understands that the go dependency requires a toolchain dependency with a greater or equal Go version.

Continuing the example, a later go get go@1.25.0 will update the toolchain to go1.25.0 as well. When the toolchain matches the go line exactly, it can be omitted and implied, so this go get will delete the toolchain line.

The same requirement applies in reverse when downgrading: if the go.mod starts at go 1.22.1 and toolchain go1.24rc1, then go get toolchain@go1.22.9 will update only the toolchain line, but go get toolchain@go1.21.3 will downgrade the go line to go 1.21.3 as well. The effect will be to leave just go 1.21.3 with no toolchain line.

The special form toolchain@none means to remove any toolchain line, as in go get toolchain@none or go get go@1.25.0 toolchain@none.

The go command understands the version syntax for go and toolchain dependencies as well as queries.

For example, just as go get example.com/widget@v1.2 uses the latest v1.2 version of example.com/widget (perhaps v1.2.3), go get go@1.22 uses the latest available release of the Go 1.22 language version (perhaps 1.22rc3, or perhaps 1.22.3). The same applies to go get toolchain@go1.22.

The go get and go mod tidy commands maintain the go line to be greater than or equal to the go line of any required dependency module.

For example, if the main module has go 1.22.1 and we run go get example.com/widget@v1.2.3 which declares go 1.24rc1, then go get will update the main module’s go line to go 1.24rc1.

Continuing the example, a later go get go@1.22.1 will downgrade example.com/widget to a version compatible with Go 1.22.1 or else remove the requirement entirely, just as it would when downgrading any other dependency of example.com/widget.

Before Go 1.21, the suggested way to update a module to a new Go version (say, Go 1.22) was go mod tidy -go=1.22, to make sure that any adjustments specific to Go 1.22 were made to the go.mod at the same time that the go line is updated. That form is still valid, but the simpler go get go@1.22 is now preferred.

When go get is run in a module in a directory contained in a workspace root, go get mostly ignores the workspace, but it does update the go.work file to upgrade the go line when the workspace would otherwise be left with too old a go line.

Managing Go version workspace requirements with go work

As noted in the previous section, go get run in a directory inside a workspace root will take care to update the go.work file’s go line as needed to be greater than or equal to any module inside that root. However, workspaces can also refer to modules outside the root directory; running go get in those directories may result in an invalid workspace configuration, one in which the go version declared in go.work is less than one or more of the modules in the use directives.

The command go work use, which adds new use directives, also checks that the go version in the go.work file is new enough for all the existing use directives. To update a workspace that has gotten its go version out of sync with its modules, run go work use with no arguments.

The commands go work init and go work sync also update the go version as needed.

To remove the toolchain line from a go.work file, use go work edit -toolchain=none.