golang/go · error
%s cannot be modified
Error message
%s cannot be modified
What it means
Returned by checkEnvWrite when `go env -w KEY=VAL` targets one of the read-only, computed variables: GOEXE, GOGCCFLAGS, GOHOSTARCH, GOHOSTOS, GOMOD, GOROOT, GOTELEMETRY, GOTELEMETRYDIR, GOTOOLDIR, GOVERSION, GOWORK. These reflect the active toolchain/session state, so the go command refuses to persist them into the go-env config file.
Source
Thrown at src/cmd/go/internal/envcmd/env.go:645
}
}
return ""
}
func checkEnvWrite(key, val string) error {
switch key {
case "GOEXE",
"GOGCCFLAGS",
"GOHOSTARCH",
"GOHOSTOS",
"GOMOD",
"GOROOT",
"GOTELEMETRY",
"GOTELEMETRYDIR",
"GOTOOLDIR",
"GOVERSION",
"GOWORK":
return fmt.Errorf("%s cannot be modified", key)
case "GOENV", "GODEBUG":
return fmt.Errorf("%s can only be set using the OS environment", key)
}
// To catch typos and the like, check that we know the variable.
// If it's already in the env file, we assume it's known.
if !cfg.CanGetenv(key) {
return fmt.Errorf("unknown go command variable %s", key)
}
// Some variables can only have one of a few valid values. If set to an
// invalid value, the next cmd/go invocation might fail immediately,
// even 'go env -w' itself.
switch key {
case "GO111MODULE":
switch val {
case "", "auto", "on", "off":
default:View on GitHub (pinned to b6b368adc5)
Solutions
- Set the variable in the OS environment (export GOROOT=...; or in the parent process env) rather than via `go env -w`.
- For GOROOT specifically: install/use the desired Go toolchain (e.g. via go toolchain directives or GOTOOLCHAIN) instead of rewriting GOROOT.
- Pick the correct user-settable equivalent — consult `go help environment` for the writable var list.
- Remove the offending line from your setup script that calls `go env -w` on a computed key.
Example fix
# before $ go env -w GOROOT=/opt/go-beta error: GOROOT cannot be modified # after — set it in the shell/CI environment instead $ GOROOT=/opt/go-beta go build ./...
Defensive patterns
Strategy: validation
Validate before calling
// Reject read-only keys before calling `go env -w`.
var readOnly = map[string]bool{
"GOEXE": true, "GOGCCFLAGS": true, "GOHOSTARCH": true,
"GOHOSTOS": true, "GOMOD": true, "GOROOT": true,
"GOTELEMETRY": true, "GOTELEMETRYDIR": true, "GOTOOLDIR": true,
"GOVERSION": true, "GOWORK": true,
}
func safeEnvW(key, val string) error {
if readOnly[key] {
return fmt.Errorf("%s cannot be set via `go env -w`; use OS env", key)
}
return runGoEnvW(key, val)
} Prevention
- Maintain a known-writable allowlist (from `go help environment`) in config tooling.
- Prefer OS environment for toolchain-location vars; reserve `go env -w` for user preferences.
- Document in CI scripts which vars are set via OS env vs go env file.
When it happens
Trigger: Invoking `go env -w GOROOT=/custom/go`, `go env -w GOVERSION=...`, `go env -w GOMOD=...`, or any other listed key in checkEnvWrite's first switch case.
Common situations: Scripts trying to relocate GOROOT via `go env -w` instead of reinstalling or setting GOROOT in the OS environment; CI images attempting to pin GOVERSION; users confused between build-computed vars and user-settable ones.
Related errors
- %s can only be set using the OS environment
- unknown go command variable %s
- invalid %s value %q
- GOPATH entry cannot start with shell metacharacter '~': %q
- GOPATH entry is relative; must be absolute path: %q
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/54f4d6b46bdeb386.
Report an issue: GitHub.