C# inherits a lot of Java's bug landscape — null reference exceptions, concurrency over shared state — and adds its own through async/await, LINQ, and the depth of the .NET standard library.

The recurring shapes: forgotten `await` on a `Task` (the same bug as JavaScript's missing-await, with the same symptoms), deadlocks from `.Result` or `.Wait()` on an async call from a UI thread, LINQ queries that look pure but mutate captured variables, and `IDisposable` types not wrapped in `using`.

Nullable reference types (`#nullable enable`) have closed a major chunk of the NRE surface for new code. The migration is incremental — the bugs hide in the not-yet-migrated parts of older codebases.

Common pitfalls

`.Result` deadlocks

`task.Result` on a UI/sync context blocks waiting for a continuation that needs the same context. Always await async code instead.

Forgotten await on Task

An async method called without `await` runs but the caller can't see exceptions or completion. The compiler warns; teams disable the warning at their peril.

Missing using

Streams, database connections, HTTP clients — anything `IDisposable` needs `using` or it leaks until the GC eventually finalizes.