HTTP Handler Not Goroutine-Safe

race condition in HTTP handler: concurrent access to shared state

Quick Answer

HTTP handlers run concurrently in separate goroutines. Shared state must be protected with a mutex or replaced with goroutine-safe alternatives.

Why This Happens

Go's net/http server handles each request in its own goroutine. If your handler reads or writes shared variables like an in-memory cache or counter, you must synchronize access. This is easily missed because the code looks sequential in each handler function.

The Problem

var visitCount int

func handler(w http.ResponseWriter, r *http.Request) {
    visitCount++ // race condition
    fmt.Fprintf(w, "Visits: %d", visitCount)
}

The Fix

var visitCount int64

func handler(w http.ResponseWriter, r *http.Request) {
    count := atomic.AddInt64(&visitCount, 1)
    fmt.Fprintf(w, "Visits: %d", count)
}

Step-by-Step Fix

  1. 1

    Identify shared state

    Find package-level or struct-level variables accessed by HTTP handlers without synchronization.

  2. 2

    Choose a sync mechanism

    Use sync/atomic for simple counters, sync.Mutex for complex state, or sync.Map for concurrent maps.

  3. 3

    Test with -race flag

    Run go test -race to verify no data races remain in your handlers.

Got the actual stack trace?

Paste it into our free AI explainer to get the cause and the fix for your specific case — no signup, nothing to install.

Explain my error

Bugsly catches this automatically

Bugsly's AI analyzes this error pattern in real-time, explains what went wrong in plain English, and suggests the exact fix — before your users even report it.

Try Bugsly free