Why this matters

The bug. List<T> is *not* thread-safe. Beyond the obvious double-add race, concurrent Adds can interleave during the internal Array.Resize and leave the list in an internally-inconsistent state — items disappearing, indices going out of bounds, full crashes under load.

The fix. A lock around both the read and the write makes the check-then-act atomic and serializes mutations. For higher-throughput cases, switch to ConcurrentBag<T> or HashSet<T> (with locking, since HashSet isn't concurrent either) — the lock here is the smallest patch that closes the race.

Heuristic. Anywhere a static collection lives outside a request scope, treat every read+write pair as suspicious. ASP.NET fans them out across worker threads, ASP.NET Core even more aggressively.

Review heuristic

Every check-then-act over shared state is a race waiting for production load. Look for read-then-write pairs that aren't inside a transaction, a lock, or an atomic CAS. If you can articulate the bug as "if two requests arrive at the same time, both will...", it's a race.

External reference: CWE-362: Concurrent Execution using Shared Resource.

MS docs: 'Thread Safety' on `List<T>`.