Open-source project
SergeyTeplyakov/ErrorProne.NET avatar
SergeyTeplyakov/ErrorProne.NET

ErrorProne.NET: Roslyn analyzers for the C# mistakes that survive compilation

Set of roslyn-based analyzers for catching common C# errors (inspired by Google's error-prone)

1,132 stars54 forksC#MIT

At a glance

What is it?
ErrorProne.NET is an MIT-licensed set of Roslyn analyzers that flag C# code which compiles but misbehaves, from async-void methods to unobserved exceptions to thread-safety claims. The judgement is that the rules are chosen by someone who has been burned by each one, and that the package is best adopted selectively because several of them encode house style rather than correctness.
Who is it for?
ErrorProne.NET fits a C# team that has been bitten by async-void exceptions, unobserved task results or a `catch` block that discards the real exception, and that wants those mistakes flagged at build time rather than in production. It does not fit a team that wants a single opinion on style, and it does not fit a codebase where the `ConfigureAwait(false)` rules conflict with an existing convention.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 47 days ago.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Correctness analyzers for code that compiles cleanly

ErrorProne.NET is a set of Roslyn-based analyzers that help you write correct code. The idea is similar to Google's error-prone, but instead of Java the analyzers focus on correctness, and possibly performance, of C# programs.

The framing matters. Roslyn analyzers normally sit in the world of style rules and suggestions, and the C# compiler already refuses a large class of mistakes. What this package targets is the residue: code that the compiler accepts and that then does something you did not intend.

Installation is a single NuGet package, `ErrorProne.NET.CoreAnalyzers`, linked from the README. There is no command line to learn and no build step to configure beyond referencing the package, because analyzers in that form attach to the compiler automatically.

The rules are documented individually, with one Markdown file per rule under `docs/Rules/`, and every entry in the README table links to its own page. That is the documentation model the project uses, and it is the right one for a rule set this size, since the interesting part of each analyzer is the reasoning rather than the syntax it matches.

The repository root shows the shape of the project: `src/` for the analyzer implementations, `docs/` for the rule documentation, `samples/` with a `ErrorProne.Samples` project, `azure-pipelines.yml` for the build, and a `ReadMe.md` with the `LICENSE` file alongside it.

The async rules are the core of the package

The largest group of rules in the README is the async analyzers, and it is not a coincidence that the package is aimed at server-side C#.

The list starts with the `ConfigureAwait` pair. EPC14 flags a `ConfigureAwait(false)` call as redundant, and EPC15 says a `ConfigureAwait(false)` must be used. These are opposite directions, and the fact that both exist tells you they are aimed at different codebases: a library that never awaits a UI context has no reason for the call, while a library that must be usable from a UI context needs it. Turning both on in the same project is a good way to get contradictory findings.

Then the genuinely dangerous ones. EPC16 warns that awaiting the result of a null-conditional expression will cause a NullReferenceException, which is the `await task?.Foo()` shape that reads as safe and is not. EPC17 flags async-void delegates, and EPC27 flags async void methods, because an exception thrown from either cannot be awaited and crashes the process rather than returning a faulted task. EPC26 says do not use tasks in a using block, since disposing a task does not cancel or observe it.

EPC31 says do not return null for Task-like types, which turns a caller awaiting your method into a NullReferenceException far from the actual mistake. EPC33 says do not use `Thread.Sleep` in async methods, and EPC35 says do not block unnecessarily in async methods, which together catch the two ways a method stalls a thread pool thread.

EPC36 is the sharpest of the set in my view, because it is not a style disagreement: do not use async delegates with `Task.Factory.StartNew` and `TaskCreationOptions.LongRunning`, since the delegate runs synchronously on the calling thread and the long-running hint does nothing the author expected. EPC32 recommends `TaskCompletionSource` should use `RunContinuationsAsynchronously`, and EPC37 says do not validate arguments in async methods, where a throw becomes a faulted task rather than a synchronous exception, and callers that do not await will never see it.

Error handling rules that catch discarded exceptions

The second large group is error handling, and several of these rules are about code that silently throws information away.

EPC12 is named for suspicious exception handling where only the 'Message' property is observed in the catch block. That is the pattern of a catch that logs `ex.Message` and drops the stack trace, which is exactly the information you need when the failure is not reproducible. EPC13 covers a suspiciously unobserved result, which is the same class of problem applied to return values: the code called something that produces a result, and nobody looked at it.

EPC34 connects to the framework's own attribute, saying a method return value marked with `MustUseResultAttribute` must be used. The compiler and the BCL already tell you that, and this rule makes it a build error rather than a suggestion.

EPC11 is a suspicious equality implementation, which catches the pattern where a type overrides `Equals` without handling the null case or without calling the base implementation, so equality quietly stops working for one side of the comparison.

Two rules use the ERP prefix and come from a different lineage, probably the Error Prone naming rather than the Error Prone.NET one. ERP021 is incorrect exception propagation and ERP022 is an unobserved exception in a generic exception handler, which is a catch of `Exception` that does nothing with it. That is the most common way an exception disappears in .NET code, and it is worth having flagged mechanically.

EPC42 rounds the group off with a serialisation concern rather than a control-flow one: a member of a data contract is not serializable, which matters if the object is going over the wire, into a cache or onto disk.

Performance and generic bug rules, and the odd ones out

The performance group is small and specific, and each rule targets a known cost.

EPC23 says avoid using `Enumerable.Contains` on a `HashSet<T>`, which is a linear scan of a data structure chosen for constant-time lookup. EPC24 covers a hash table unfriendly type used as a key, which is a type whose `GetHashCode` and `Equals` are expensive or inconsistent, so every dictionary operation pays for it. EPC25 says avoid using the default `Equals` or `HashCode` implementation from structs, which is the warning that the compiler emits when a struct holds a reference type and reflection-based equality kicks in.

The generic bugs and code smells group is where the package shows its personality, because some of these are opinions.

EPC19 says observe and dispose a `CancellationTokenRegistration` to avoid memory leaks, which is a real bug in long-lived services. EPC20 says avoid using the default `ToString` implementation, which produces type names in log output that nobody can search for. EPC28 says do not use `ExcludeFromCodeCoverage` on partial classes, and EPC29 says `ExcludeFromCodeCoverageAttribute` should provide a message, which are conventions about how teams use that attribute. EPC30 catches a method that calls itself recursively, which is usually a bug but occasionally a deliberate pattern.

The concurrency group has a single rule, ERP031: the API is not thread-safe. That is a broad claim, and it is the rule most likely to need project-level configuration rather than a global switch, because the analyzer needs to know which of your public types are meant to be safe.

The two EventSource rules, ERP041 that an EventSource class should be sealed and ERP042 that an EventSource implementation is not correct, are the clearest example of domain knowledge in the package. They encode knowledge about `System.Diagnostics.Tracing.EventSource` that a general-purpose analyzer would not have.

How to adopt it without drowning in findings

The package is easy to add and easy to regret, and the reason is visible in the rule list.

Several of these rules are not about correctness at all. EPC14 and EPC15 are mutually contradictory style rules about `ConfigureAwait(false)`. EPC29 asks for a message on a coverage attribute. EPC41 asks for EventSource classes to be sealed. A team that enables everything will get findings on code it has deliberately written, and the first reaction will be to disable the whole package.

The realistic approach is to start with the correctness rules and the error handling rules, since those are the ones with a defensible right answer, and leave the style ones off until the team has agreed on a house convention. The `EPC` and `ERP` prefixes and the grouping in the README make that selection possible, because the categories are already separated into async, generic bugs and code smells, concurrency, error handling and performance.

There is a second practical issue: the analyzer has to load. Roslyn analyzers distributed as NuGet packages are picked up by the compiler from the project reference, and the version of the analyzer package has to be compatible with the Roslyn version that ships with your SDK. An analyzer built against a newer Roslyn than your SDK has will simply not run, and the symptom is silence rather than an error.

The `samples/ErrorProne.Samples` project is the place to check that, since it is a project with the package referenced and nothing else. Building it and seeing the expected diagnostics is the fastest verification that the analyzers are active in your build at all.

How this differs from the built-in analyzers and from JetBrains ReSharper

The comparison that matters is with the analyzers you already have, and the difference is one of intent rather than of mechanism.

The .NET SDK ships a set of built-in analyzers, and the compiler itself enforces a set of rules. Those are designed to be uncontroversial: they catch things that are almost always wrong, and they do not presume a coding standard. The `Nullable` reference types analysis, for example, is a language feature with a diagnostic attached, and the built-in CA rules are pattern-based.

ErrorProne.NET sits closer to the other end. It is inspired by Google's error-prone for Java, and that project was explicitly built to catch errors in code that a compiler cannot, using whole-program reasoning where possible. The rules here reflect the same philosophy: rules about `async void` methods and about `Task.Factory.StartNew` with a long-running option are not syntax patterns, they are statements about intent.

The commercial alternative in the .NET space is the ReSharper code analysis from JetBrains, and the difference there is commercial rather than philosophical. ReSharper covers correctness, performance, code quality and a very large style and maintainability set, with a commercial licence, a much larger body of rules and a mature settings model. ErrorProne.NET is MIT, is a smaller and more opinionated set, and has no licensing cost and no company behind a support contract.

So the practical comparison is: for correctness-only rules with no licence cost, this package is a small, sharp option. For a full static analysis programme with configuration, refactorings and a commercial relationship, ReSharper is the other thing you would be comparing it to. What this package does not do is replace a general-purpose analyser suite, and the README does not claim that it does.

Editorial conclusion

ErrorProne.NET fits a C# team that has been bitten by async-void exceptions, unobserved task results or a `catch` block that discards the real exception, and that wants those mistakes flagged at build time rather than in production. It does not fit a team that wants a single opinion on style, and it does not fit a codebase where the `ConfigureAwait(false)` rules conflict with an existing convention. Verify first that the rules you care about are enabled at the severity you want, since the `EPC` and `ERP` identifiers cover both correctness and convention, that the analyzer package version you reference matches the Roslyn version your SDK ships so the analyzers actually load, and that enabling the package does not flood the build with findings on code you have already decided to keep.

Frequently asked questions

How do I add ErrorProne.NET analyzers to a C# project?

Add the `ErrorProne.NET.CoreAnalyzers` NuGet package to the project. The README links the package page directly, and no command line or build configuration beyond the package reference is required, because the analyzers attach to the Roslyn compiler automatically.

What kinds of bugs do the ErrorProne.NET analyzers catch?

The rules cover async mistakes such as async void methods and awaiting a null-conditional expression, error handling problems such as a catch block that only observes the Message property, concurrency issues, performance problems such as using Enumerable.Contains on a HashSet, and generic smells such as an unobserved method result.

What is the difference between the EPC and ERP rule identifiers in ErrorProne.NET?

The identifiers come from two lineages. The EPC rules are the Error Prone.NET additions, and the ERP rules, such as ERP021 for incorrect exception propagation and ERP031 for a non-thread-safe API, come from the Error Prone naming. Each rule has its own Markdown page under `docs/Rules/` linked from the README tables.

Is ErrorProne.NET related to Google's error-prone project?

The idea is similar, but the implementation is separate. The README says the idea is similar to Google's error-prone, but instead of Java the analyzers focus on correctness, and possibly performance, of C# programs.

How do I check that the ErrorProne.NET analyzers are actually running?

Build the `samples/ErrorProne.Samples` project, which has the package referenced and nothing else. If the expected diagnostics do not appear, the analyzers have not loaded, which usually means the analyzer package version does not match the Roslyn version shipped with your SDK.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. SergeyTeplyakov/ErrorProne.NET on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/sergeyteplyakov-errorprone-net.svg)](https://hysenlabs.com/projects/sergeyteplyakov-errorprone-net)

Was this useful?