Go 1.27 refuses to build with removed GODEBUG settings
By Nihar Ranjan Das · Sat Oct 17 2026 · 10 min read · 0 views
View as a Web StorySoftware#upgrade guide#go#golang#go 1.27#godebug

Go 1.27 refuses to build with removed GODEBUG settings
Why does Go 1.27 fail with "removed GODEBUG set to old value"?
Go 1.27 fails because the go command now rejects any removed GODEBUG setting that still carries its old value. The verification happens during module loading, which precedes compilation entirely, so continuous integration pipelines surface the problem immediately. Your code is not at fault; a configuration line is.
GODEBUG is a Go mechanism that lets a program opt back into an older runtime or library behavior. It is set with a godebug line in go.mod, a //go:debug comment in a main package, or an environment variable. The Go team keeps each setting for a limited time, then deletes it.
Go 1.27 was released in August 2026. The Go 1.27 release notes list the removals. They also say the go command accepts a removed setting in go.mod only if it has the final default value. Any other value fails the build.
This is the exact text we got from Go 1.27.2 for a godebug asynctimerchan=1 line:
go: error loading go.mod:
go.mod:5: removed GODEBUG "asynctimerchan" set to old value "1" (https://go.dev/doc/godebug#go-127)
A //go:debug asynctimerchan=1 comment at the top of main.go fails differently. The compiler reports invalid //go:debug: removed GODEBUG "asynctimerchan" set to old value "1". The message names the file and line, so it is easy to find.
For example, a service that kept //go:debug tlsrsakex=1 for a legacy payment terminal will fail the same way. The fix for both is one deletion. Remove the line, or change the value to the new default. We confirmed that godebug asynctimerchan=0 builds cleanly on Go 1.27.2.
Which GODEBUG settings did Go 1.27 remove?
Method: we wrote a ten-line go.mod for each setting and ran go build with Go 1.26.9 and Go 1.27.2. We recorded the first line of output. Nothing else in the module changed between runs.
Go 1.27 removed eight settings. One of them, tlskyber, was actually removed in Go 1.24 but went undocumented until now. The table shows what we tested and what each result was.
| Setting | Old value we set | Go 1.26.9 | Go 1.27.2 |
|---|---|---|---|
asynctimerchan |
1 | builds | fails |
tlsrsakex |
1 | builds | fails |
tls3des |
1 | builds | fails |
tls10server |
1 | builds | fails |
tlsunsafeekm |
1 | builds | fails |
x509keypairleaf |
0 | builds | fails |
gotypesalias |
0 | builds | fails |
tlskyber |
0 | unknown godebug |
fails (points to the go-124 history) |
asynctimerchan |
0 (new default) | builds | builds |
panicnil |
1 | builds | builds |
Source: our test of one-line go.mod files, October 16, 2026. The removals match the official removal list.

Advertisement
Two details matter. First, panicnil=1 still works, so not every godebug line breaks. Second, tlskyber is a trap: Go 1.26.9 does not even recognize it, so a copy-pasted line from an old blog post can sit in a repository unnoticed.
How do you find the offending lines before you upgrade?
Search three places: go.mod files, Go source files, and deployment configuration. A single grep over the repository covers the first two. The third needs a look at manifests.
Consider a repository with twelve services. Which of them still carries a stale line? Run this from the repository root:
grep -rnE '^\s*godebug|//go:debug' --include=go.mod --include=*.go .
grep -rnE 'asynctimerchan|tlsunsafeekm|tlsrsakex|tls3des|tls10server|x509keypairleaf|gotypesalias|tlskyber' .
The first command lists every godebug and //go:debug line. The second finds the removed names anywhere, including Dockerfiles, Helm charts and CI files. In a multi-module repository, run it once at the top and read every hit. Each go.mod has its own godebug block.
You can also ask the toolchain directly. After installing Go 1.27, run go list ./... in each module. It loads go.mod and fails fast with the line number, which makes it a cheap check for a CI job.
A shell loop over several modules works well in a monorepo:
for m in $(find . -name go.mod -exec dirname {} \;); do
(cd "$m" && go list ./... >/dev/null) || echo "FAILED: $m"
done
Run that with Go 1.27 on a branch before you change the go directive. It tells you which modules need edits.
What happens when a GODEBUG environment variable is set in production?
The build passes and the binary crashes at startup. Go 1.27 treats a removed setting in the process environment as fatal. We built a small program with Go 1.27.2 and ran it with GODEBUG=asynctimerchan=1:
fatal error: removed GODEBUG "asynctimerchan" set to old value "1" in environment (https://go.dev/doc/godebug#go-127)
goroutine 1 [running, locked to thread]:
However, this is the case that causes outages. A Kubernetes env: entry, a systemd unit or a Dockerfile ENV line is invisible to go build. CI stays green. The new image rolls out, and every pod enters a crash loop.
The Go 1.27 toolchain's own test suite shows the same rule. Its godebug_removed script expects a startup panic when a removed setting appears in the environment, and a build failure when it appears in go.mod, per the Go source tree.
Therefore, our advice as someone who ships Go services is this: grep the manifests, not only the code. Search every Helm values file and every compose file. If you deploy with a platform that injects GODEBUG for you, check its defaults too.
Is there a silent change for older go.mod versions?
Yes. A module whose go line is below 1.23 now gets unbuffered timer channels, and nothing warns you. This matters because asynctimerchan defaulted to the old behavior for those modules until Go 1.27.
We wrote a four-line program that creates a one-millisecond timer, sleeps 20 milliseconds, and prints len(t.C). The go.mod said go 1.22 and set no godebug line.
| Toolchain | go.mod says |
len(t.C) after the timer fired |
|---|---|---|
| Go 1.26.9 | go 1.22 |
1 |
| Go 1.27.2 | go 1.22 |
0 |
Source: our test program, October 16, 2026.

The old behavior put a buffered value into the channel, so len(t.C) could read 1. The new behavior hands the value over only when a receiver is ready. Code that polls len(t.C) to ask whether a timer fired will now always read 0.
In addition, search for len(t.C), len(ticker.C) and any Reset or Stop code that drains a channel by hand. The Go 1.23 release notes introduced the unbuffered design so Reset and Stop need no draining. If your code still drains, it can now block or skip a tick.
What do the removed TLS settings change?
They remove the ability to re-enable weak TLS behavior with a GODEBUG flag. The affected names are tls10server, tls3des, tlsrsakex and tlsunsafeekm. A service that kept one of these in go.mod was making a deliberate exception for an old client.
TLS 1.0 is a transport security protocol from 1999 that the IETF formally deprecated in 2021 under RFC 8996. RSA key exchange is a TLS handshake mode without forward secrecy. 3DES is a 64-bit block cipher with known birthday attacks. Current browsers no longer negotiate them.
Go 1.27 no longer lets you flip them back on through GODEBUG. If a partner or a legacy device still needs one, you now need a separate terminator in front of it. We did not test handshake behavior; our test covered only the go.mod gate, which fails the build.
For a European or US team under PCI DSS or a similar standard, such as the NIST TLS guidelines, this removal is mostly good news. The exception lines usually exist as audit findings. Removing them closes the finding and simplifies the story you tell an auditor.
Do GODEBUG lines in a dependency's go.mod matter?
No, they do not. Only the main module's go.mod counts. We built an app that depends on a local module whose own go.mod says godebug asynctimerchan=1. Both Go 1.26.9 and Go 1.27.2 built the app without a message.
This matters for large repositories. A stale line in a vendored library or a shared internal module will not stop your build. It also will not change behavior, because the Go command reads the godebug block of the main module only. Fix your own modules first, and leave dependencies alone.
Workspaces need a closer look. With a go.work file, the workspace file can carry its own godebug block. Search it with the same grep, because it is easy to forget when you only scan go.mod files.
How do you fix each case step by step?
Delete the line, or set the removed name to its new default. Each location has its own edit, and the edits are small. Here is a before and after for the three places we tested.
In go.mod, remove the line:
module example.com/service
go 1.23
-godebug asynctimerchan=1
In a main package, remove the comment:
-//go:debug asynctimerchan=1
package main
In a Kubernetes manifest, remove the variable from the container spec:
env:
- - name: GODEBUG
- value: "asynctimerchan=1"
- name: PORT
value: "8080"
After each edit, run go build ./... and your tests. If the removed setting protected a real behavior, the test that relied on it will now fail in a visible place. That failure is the useful part: it tells you which code needs a real fix instead of a flag.
For timer code, replace channel-length checks with a select. Ask the timer a question with select { case <-t.C: ... default: ... }. That form works on every Go version, so it is safe to ship before the upgrade.
Should you upgrade to Go 1.27 now or wait?
Should your team move this week? Upgrade now if your repository passes the grep and your manifests are clean. Wait a sprint if you found a removed setting that still protects a real client. The failure is loud in the build, which is the good kind.
Use this order:
- Run the two
grepcommands and fix every hit. - Run the
go listloop with Go 1.27 on a branch. - Check deployment manifests for
GODEBUGvariables. - Search for
len(t.C)in any module with agoline below 1.23. - Bump the
godirective last, becausego mod tidychanges behavior for modules at 1.27 or later.

The step order matters because step 5 changes more than timers. The release notes say go mod tidy merges duplicate require blocks for modules at go 1.27 or later. They also say go test now runs the stdversion vet check by default, which can add new test failures. Doing it last keeps those diffs separate from the GODEBUG cleanup.
If you cannot upgrade yet, stay on Go 1.26. Check the Go release policy for how long that line receives security fixes.
Test two more Go 1.27 changes in the same upgrade pass. Our measurements are in Does Go 1.27's json v2 break your LLM parsing? We tested and in Go 1.27 now drains HTTP bodies on Close: what we measured. Neither blocks the upgrade.
What if you cannot remove a setting today?
Pin the toolchain and plan the removal. Can your team stay on Go 1.26 for one more release cycle? Then keep the line, set toolchain go1.26.9 in go.mod, and schedule the cleanup. Go 1.26 will keep building the module as it does now.
Do not rely on this for long. Each Go release line gets security fixes for a limited time. For example, a service that waits a year will eventually run an unsupported toolchain. Treat the pin as a one-sprint bridge, and open a ticket for the owner of each removed setting, because organizations that postpone infrastructure compatibility work usually accumulate considerably larger migration obligations later.
What is new here that the release notes do not say?
The notes explain the rule. They do not show where each failure happens or which one reaches production. Our test adds three things you can use today:
- A failure map. go.mod and source comments fail at build time; environment variables fail at process start.
- A silent change. Modules below
go 1.23change timer behavior with no message. - A fast check. Running
go list ./...with Go 1.27 finds the go.mod problems in seconds.
That third item is the one we would add to CI first. It costs one job and catches the line before a release does.
Advertisement
FAQ
Why does Go 1.27 say "removed GODEBUG set to old value"?
The `go` command found a removed GODEBUG setting with a value other than the final default. Go 1.27 accepts a removed setting only at its default value. Delete the `godebug` line in `go.mod`, or the `//go:debug` comment, and rebuild.
Can I keep asynctimerchan=1 in Go 1.27?
No. Go 1.27 always uses unbuffered timer channels, whatever GODEBUG or the go.mod version says. The line `asynctimerchan=1` fails the build, and the same value in the environment kills the process at startup. Only `asynctimerchan=0` is accepted.
Does a GODEBUG environment variable break the build?
No, it breaks the running binary. The build ignores the variable. A Go 1.27 program started with a removed setting at its old value ends with a `fatal error` before `main` runs, so check your deployment manifests too.
Which Go 1.27 GODEBUG settings are still allowed?
Settings that were not removed still work. In our test, `panicnil=1` built cleanly on Go 1.27.2. Removed names include `asynctimerchan`, `tlsunsafeekm`, `tlsrsakex`, `tls3des`, `tls10server`, `x509keypairleaf`, `gotypesalias` and `tlskyber`.
How do I check a whole monorepo for this error?
Install Go 1.27 and run `go list ./...` inside every directory that holds a `go.mod`. It loads the file and reports the line number of any removed setting. Pair it with a `grep` over manifests for the environment-variable case.
Comments
Loading…
Sign in to join the conversation.
Related posts

Does Go 1.27's json v2 break your LLM parsing? We tested
Go 1.27 runs encoding/json on the v2 engine. We tested 11 LLM-style payloads on Go 1.26.9 and 1.27.2: v1 behaved the same, v2 is stricter, Unmarshal is 2.3x faster.
Sat Oct 17 2026 · 10 min read · 0 views

Go 1.27 now drains HTTP bodies on Close: what we measured
Go 1.27 drains unread HTTP/1 response bodies on Close, up to 256 KiB or 50 ms. We measured connection reuse on Go 1.26.9 and 1.27.2 and found a hard cliff at 257 KiB.
Sat Oct 17 2026 · 9 min read · 0 views

Remotion vs HyperFrames: we timed renders and priced both
Remotion renders our test video about 3x faster than HyperFrames, but HyperFrames has no licence fee. Measured render times, a cost table and a pick-by-team-size guide.
Sat Oct 17 2026 · 8 min read · 0 views