Go t.Parallel() Shared-State Races - Fix Flaky Parallel Tests in CI
Calling t.Parallel() lets tests run concurrently. When several parallel tests touch the same global variable, shared file, or process-wide setting, they race - producing intermittent failures or a data-race report that only shows up under -race and load.
What this error means
Parallel tests pass alone but fail intermittently together, with a DATA RACE on a shared variable, a clobbered shared temp file, or a t.Setenv panic. The flakiness is timing-dependent and worse under -race or higher -parallel.
==================
WARNING: DATA RACE
Write at 0x... by goroutine 9:
app/cache_test.go:21 +0x...
# or:
panic: testing: t.Setenv called after t.Parallel; cannot set environment in parallel testCommon causes
Parallel tests sharing mutable global state
Two t.Parallel() tests read/write the same package-level variable or singleton, so their concurrent access races.
t.Setenv combined with t.Parallel
t.Setenv mutates process-wide environment and is forbidden in parallel tests; Go panics when both are used in the same test.
A shared file or temp path across parallel tests
Parallel tests writing the same fixed temp file or directory clobber each other non-deterministically.
How to fix it
Give each parallel test isolated state
Use per-test locals and t.TempDir() so concurrent tests do not share mutable resources.
func TestA(t *testing.T) {
t.Parallel()
dir := t.TempDir() // unique per test
// operate on local state, not package globals
}Do not mix t.Setenv with t.Parallel
Keep env-mutating tests serial, or inject configuration instead of using process env.
func TestEnv(t *testing.T) {
// no t.Parallel() here
t.Setenv("API_URL", "http://localhost")
}Run with the race detector to surface the conflict
go test -race -count=1 ./...How to prevent it
- Keep parallel tests free of shared mutable globals.
- Use
t.TempDir()and per-test locals for isolation. - Never combine
t.Setenvwitht.Parallel(); run env tests serially.