Production-focused Roslyn analyzers and code fixes for .NET HttpClient, IHttpClientFactory, typed and named clients, Polly, and Microsoft.Extensions.Http.Resilience.
Catch outbound HTTP reliability bugs during development—not after deployment. The analyzer detects socket-exhaustion risks, stale DNS clients, DI lifetime leaks, unsafe retries, undisposed responses and streams, sync-over-async, dropped cancellation tokens, unbounded fan-out, and fragile named-client strings.
The package is analyzer-only and adds no runtime dependency to your application.
dotnet add package HttpClient.Resilience.AnalyzersOr add an explicit package reference:
<PackageReference Include="HttpClient.Resilience.Analyzers" Version="0.1.140" PrivateAssets="all" />PrivateAssets="all" prevents the analyzer from flowing to projects that consume your project.
dotnet buildDiagnostics appear in supported IDEs and in normal command-line or CI builds. No application code, service registration, or runtime configuration is required.
For example, this typed client can send a non-idempotent POST through the standard retry pipeline:
services.AddHttpClient<PaymentsClient>()
.AddStandardResilienceHandler();
public sealed class PaymentsClient(HttpClient httpClient)
{
public Task<HttpResponseMessage> CreateAsync(
CancellationToken cancellationToken) =>
httpClient.PostAsync("/payments", null, cancellationToken);
}HCR041 reports the risk. If unsafe methods are not deliberately idempotent, disable their retries:
services.AddHttpClient<PaymentsClient>()
.AddStandardResilienceHandler(options =>
{
options.Retry.DisableForUnsafeHttpMethods();
});Several rules include automatic code fixes; every diagnostic links to a rule page explaining the risk, detection scope, safe alternatives, and suppression guidance.
| Area | Examples of detected risk |
|---|---|
| Client lifetime | Per-request HttpClient creation, stale long-lived connections, cached factory clients |
| Dependency injection | Typed clients held by singletons, duplicate registrations, scoped state captured by handlers |
| Resilience and Polly | Duplicate handlers, unsafe-method retries, per-request pipeline construction |
| Response ownership | Undisposed ResponseHeadersRead responses and HTTP content streams |
| Request correctness | Unchecked failure responses, shared default-header mutation, missing cancellation |
| Async and concurrency | Sync-over-async and obvious unbounded HTTP fan-out |
| Typed and named clients | Relative URLs without BaseAddress and duplicated string client names |
The rules intentionally focus on concrete production risks. Heuristic checks use a lower default severity, and deliberate exceptions can be configured per rule.
| Rule | Category | Detects | Default | Fix |
|---|---|---|---|---|
HCR001 |
Lifetime | Creating and disposing HttpClient in request paths |
Warning | Partial |
HCR002 |
Lifetime | Long-lived manual clients without PooledConnectionLifetime |
Warning | Yes |
HCR003 |
Lifetime | Cached IHttpClientFactory.CreateClient() results |
Warning | Guide |
HCR004 |
Typed clients | Typed clients injected into singleton services | Warning | Guide |
HCR005 |
Typed clients | Duplicate typed-client registrations | Warning | Yes |
HCR020 |
Handlers | DelegatingHandler capturing scoped request data |
Warning | Guide |
HCR040 |
Resilience | Duplicate resilience handlers in one client pipeline | Warning | Yes |
HCR041 |
Resilience | Unsafe HTTP methods retried without explicit configuration | Warning | Yes |
HCR060 |
Response lifetime | Undisposed ResponseHeadersRead responses |
Warning | Yes |
HCR061 |
Response lifetime | Response content read before checking success | Warning | Partial |
HCR062 |
Response lifetime | Per-request values written to DefaultRequestHeaders |
Warning | Guide |
HCR063 |
Response lifetime | Sync-over-async around outbound HTTP | Warning | Partial |
HCR064 |
Response lifetime | HTTP calls that omit an available CancellationToken |
Warning | Yes |
HCR080 |
Concurrency | Obvious unbounded Task.WhenAll HTTP fan-out |
Suggestion | Guide |
HCR081 |
Response lifetime | Undisposed streams returned from HTTP content | Warning | Partial |
HCR082 |
Resilience | Per-request resilience pipeline construction | Warning | Guide |
HCR083 |
Typed clients | Relative URLs used without a configured BaseAddress |
Warning | Guide |
HCR084 |
Named clients | Duplicated string literals for named-client names | Warning | Guide |
See the rules index for categories and recommended rollout order, or open an individual rule for exact detection details and limitations.
Set any rule's severity in your repository's .editorconfig:
[*.cs]
dotnet_diagnostic.HCR041.severity = error
dotnet_diagnostic.HCR080.severity = suggestionSupported values include error, warning, suggestion, silent, and none. Prefer a targeted override over disabling the analyzer package.
The repository includes ready-made starting profiles:
| Profile | Best for |
|---|---|
default.editorconfig |
New services and teams ready to act on production-safety warnings |
brownfield-adoption.editorconfig |
Existing applications that need a lower-noise first pass |
strict-ci.editorconfig |
Clean repositories that want production-safety findings to fail CI |
library-author.editorconfig |
Libraries with stricter response and stream ownership requirements |
For one intentional exception, keep the reason next to the code:
#pragma warning disable HCR041 // The endpoint enforces idempotency keys, so retries are safe.
services.AddHttpClient<PaymentsClient>()
.AddStandardResilienceHandler();
#pragma warning restore HCR041See configuration, adoption, and the false-positive policy for team rollout guidance.
- Install the package and build without changing CI gates.
- For an established codebase, begin with the brownfield profile.
- Fix high-confidence findings in critical outbound paths first.
- Suppress deliberate exceptions narrowly and record why they are safe.
- Move to the default profile, then strict CI, once the baseline is clean.
No. It statically checks how your code uses HttpClient, IHttpClientFactory, Polly, and the .NET resilience libraries. It does not add or replace a runtime resilience pipeline.
No. The analyzer assembly runs at compile time and the package contains no application runtime dependency.
Yes. Configure dotnet_diagnostic.HCRxxx.severity in .editorconfig. Each rule page also explains when a narrow suppression may be appropriate.
Check the rule's documented detection scope, reduce its severity if needed, and open an issue with a minimal reproduction. The project's false-positive policy treats diagnostic trust as a release requirement.
- Documentation hub
- All analyzer rules
- Implementation status and limitations
- Contributing
- Code of Conduct
- Support
- Security policy
- MIT license
Contributions and focused bug reports are welcome. Please include the rule ID and a minimal C# reproduction when reporting an analyzer false positive or false negative.
