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.

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