| name | dotnet-agent-gotchas |
| description | Flags agent mistakes in .NET code -- async misuse, NuGet errors, deprecated APIs, DI. |
dotnet-agent-gotchas
Common mistakes AI agents make when generating or modifying .NET code, organized by category. Each category provides a
brief warning, anti-pattern code, corrected code, and a cross-reference to the canonical skill that owns the deep
guidance. This skill does NOT provide full implementation walkthroughs -- it surfaces the mistake and points to the
right skill.
Scope
- Common async/await, NuGet, deprecated API, and DI mistakes agents make
- Anti-pattern / corrected-code pairs per category
- Cross-references to canonical skills for deep guidance
Out of scope
- Deep async/await patterns -- see [skill:dotnet-csharp-async-patterns]
- Full dependency injection guidance -- see [skill:dotnet-csharp-dependency-injection]
- NRT usage patterns -- see [skill:dotnet-csharp-nullable-reference-types]
- Source generator authoring -- see [skill:dotnet-csharp-source-generators]
- Test framework features -- see [skill:dotnet-testing-strategy]
- Security vulnerability mitigation -- see [skill:dotnet-security-owasp]
Prerequisites
.NET 8.0+ SDK. Familiarity with SDK-style projects and C# language features.
Cross-references: [skill:dotnet-csharp-async-patterns], [skill:dotnet-csharp-dependency-injection],
[skill:dotnet-csharp-nullable-reference-types], [skill:dotnet-csharp-source-generators],
[skill:dotnet-testing-strategy], [skill:dotnet-security-owasp].
Category 1: Async/Await Misuse
Warning: Agents frequently block on async methods using .Result or .Wait(), causing deadlocks in ASP.NET Core
and UI contexts. Another common mistake is fire-and-forget calls that silently swallow exceptions.
Anti-Pattern
public Order GetOrder(int id)
{
var order = _repository.GetOrderAsync(id).Result;
return order;
}
public void ProcessOrder(Order order)
{
_ = _emailService.SendConfirmationAsync(order);
}
```text
### Corrected
```csharp
public async Task<Order> GetOrderAsync(int id, CancellationToken ct = default)
{
var order = await _repository.GetOrderAsync(id, ct);
return order;
}
public async Task ProcessOrderAsync(Order order, CancellationToken ct = default)
{
await _emailService.SendConfirmationAsync(order, ct);
}
```text
See [skill:dotnet-csharp-async-patterns] for full async/await guidance including `ValueTask`, `ConfigureAwait`, and
cancellation propagation.
---
## Category 2: NuGet Package Errors
**Warning:** Agents generate incorrect package names, reference pre-release versions without opt-in, or add packages
that have been deprecated/replaced. ASP.NET Core shared-framework packages must match the project TFM major version.
### Anti-Pattern
```xml
<!-- WRONG: package name does not exist (correct: Microsoft.EntityFrameworkCore) -->
<PackageReference Include="EntityFrameworkCore" Version="9.0.0" />
<!-- WRONG: hardcoded version for shared-framework package -- must match TFM -->
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.0" />
<!-- This breaks on net8.0 projects -->
<!-- WRONG: agents add Swashbuckle by default; .NET 9+ templates use built-in OpenAPI -->
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.0.0" />
<!-- Swashbuckle is still valid when Swagger UI is needed, but not the default choice -->
```text
### Corrected
```xml
<!-- CORRECT: exact package ID -->
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
<!-- CORRECT: use version variable or central package management to match TFM -->
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<!-- Version managed via Directory.Packages.props matching project TFM -->
<!-- CORRECT: .NET 9+ templates prefer built-in OpenAPI support -->
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
<!-- Swashbuckle remains a valid choice when Swagger UI features are needed -->
```text
See [skill:dotnet-csproj-reading] for project file conventions and central package management guidance.
---
## Category 3: Deprecated API Usage
**Warning:** Agents generate code using deprecated and insecure APIs: `BinaryFormatter` (CVE-prone deserialization),
`WebClient` (replaced by `HttpClient`), and older cryptography APIs (`RNGCryptoServiceProvider`,
`SHA1CryptoServiceProvider`).
### Anti-Pattern
```csharp
var formatter = new BinaryFormatter();
formatter.Serialize(stream, data);
var client = new WebClient();
var html = client.DownloadString("https://example.com");
using var rng = new RNGCryptoServiceProvider();
rng.GetBytes(buffer);
```text
### Corrected
```csharp
var json = JsonSerializer.Serialize(data);
await File.WriteAllTextAsync("data.json", json);
public class MyService(HttpClient httpClient)
{
public async Task<string> GetHtmlAsync(CancellationToken ct = default)
=> await httpClient.GetStringAsync("https://example.com", ct);
}
RandomNumberGenerator.Fill(buffer);
```text
See [skill:dotnet-security-owasp] for the full deprecated security pattern catalog and OWASP mitigations.
---
## Category 4: Project Structure Mistakes
**Warning:** Agents use wrong SDK types, add `PackageReference` entries for framework-included libraries, or create
broken `ProjectReference` paths.
### Anti-Pattern
```xml
<!-- WRONG: using Microsoft.NET.Sdk for a web project -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<!-- Missing WebApplication APIs, Kestrel, etc. -->
</Project>
<!-- WRONG: referencing a package already in the shared framework -->
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.0" />
<!-- This is included in Microsoft.NET.Sdk.Web; explicit reference causes version conflicts -->
<!-- WRONG: relative path that doesn't match actual project location -->
<ProjectReference Include="..\..\Core\MyApp.Core.csproj" />
<!-- Actual location is ../MyApp.Core/MyApp.Core.csproj -->
```csharp
### Corrected
```xml
<!-- CORRECT: use the Web SDK for ASP.NET Core projects -->
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
</Project>
<!-- CORRECT: don't add explicit PackageReference for shared-framework packages -->
<!-- Microsoft.Extensions.Logging is implicitly available via Sdk.Web -->
<!-- CORRECT: verify the actual project path before adding a reference -->
<ProjectReference Include="..\MyApp.Core\MyApp.Core.csproj" />
```csharp
See [skill:dotnet-project-structure] for SDK types, project organization, and project reference conventions.
---
## Category 5: Nullable Reference Type Annotation Errors
**Warning:** Agents misuse the null-forgiving operator (`!`) to silence warnings instead of fixing nullability, or
forget to enable the nullable context.
### Anti-Pattern
```csharp
public string GetUserName(int id)
{
var user = _db.Users.Find(id);
return user!.Name;
}
public string? GetOptionalValue() => null;
```csharp
### Corrected
```csharp
public string GetUserName(int id)
{
var user = _db.Users.Find(id);
if (user is null)
{
throw new InvalidOperationException($"User {id} not found.");
}
return user.Name;
}
```text
```xml
<!-- CORRECT: enable nullable context in .csproj -->
<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>
```csharp
See [skill:dotnet-csharp-nullable-reference-types] for full NRT usage patterns and annotation strategies.
---
## Category 6: Source Generator Misconfiguration
**Warning:** Agents forget to mark classes as `partial` when source generators need to augment them, or use incorrect
output types that prevent generator output from compiling.
### Anti-Pattern
```csharp
[JsonSerializable(typeof(WeatherForecast))]
internal class WeatherJsonContext : JsonSerializerContext
{
}
[LoggerMessage(EventId = 1, Level = LogLevel.Information, Message = "Processing {Item}")]
public static partial struct LogMessages
{
}
```text
### Corrected
```csharp
[JsonSerializable(typeof(WeatherForecast))]
internal partial class WeatherJsonContext : JsonSerializerContext
{
}
public static partial class Log
{
[LoggerMessage(EventId = 1, Level = LogLevel.Information, Message = "Processing {Item}")]
public static partial void ProcessingItem(ILogger logger, string item);
}
```text
See [skill:dotnet-csharp-source-generators] for source generator configuration, diagnostics, and debugging.
---
## Category 7: Trimming/AOT Warning Suppression
**Warning:** Agents suppress trimming and AOT warnings with `#pragma` or `[UnconditionalSuppressMessage]` instead of
fixing the underlying reflection/dynamic usage. Suppression hides runtime failures in published apps.
### Anti-Pattern
```csharp
#pragma warning disable IL2026
var type = Type.GetType(typeName);
var instance = Activator.CreateInstance(type!);
#pragma warning restore IL2026
```csharp
### Corrected
```csharp
public T CreateInstance<T>() where T : new()
{
return new T();
}
public object CreateInstance(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type)
{
return Activator.CreateInstance(type)
?? throw new InvalidOperationException($"Cannot create {type.Name}");
}
```text
```xml
<!-- CORRECT: enable trim/AOT analyzers to catch issues early -->
<!-- For apps: -->
<PublishTrimmed>true</PublishTrimmed>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<!-- For libraries: -->
<IsTrimmable>true</IsTrimmable>
<!-- IsTrimmable auto-enables trim analyzer for libraries -->
```text
See [skill:dotnet-csproj-reading] for MSBuild property guidance on trimming and AOT configuration.
---
## Category 8: Test Organization Anti-Patterns
**Warning:** Agents put test classes in production projects, use wrong test SDK configurations, or mix test framework
attributes incorrectly.
### Anti-Pattern
```csharp
namespace MyApp.Api;
public class OrderServiceTests
{
[Fact]
public void CalculateTotal_ReturnsCorrectSum() { }
}
```text
```xml
<!-- WRONG: test project missing Microsoft.NET.Test.Sdk and runner -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="xunit.v3" Version="3.2.2" />
<!-- Missing Microsoft.NET.Test.Sdk and runner -- dotnet test will find zero tests -->
</ItemGroup>
</Project>
```text
### Corrected
```xml
<!-- CORRECT: test project in tests/ directory with proper configuration -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="xunit.v3" Version="3.2.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\MyApp.Api\MyApp.Api.csproj" />
</ItemGroup>
</Project>
```csharp
See [skill:dotnet-testing-strategy] for test organization, naming conventions, and test type decision guidance.
---
## Category 9: DI Registration Errors
**Warning:** Agents forget to register services, use wrong lifetimes (singleton capturing scoped), or create captive
dependencies that cause memory leaks and concurrency bugs.
### Anti-Pattern
```csharp
builder.Services.AddSingleton<OrderProcessor>();
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
public class OrderProcessor(IOrderRepository repo)
{
public async Task ProcessAsync(int orderId, CancellationToken ct)
{
var order = await repo.GetByIdAsync(orderId, ct);
}
}
```text
### Corrected
```csharp
builder.Services.AddScoped<OrderProcessor>();
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
builder.Services.AddSingleton<OrderProcessor>();
public class OrderProcessor(IServiceScopeFactory scopeFactory)
{
public async Task ProcessAsync(int orderId, CancellationToken ct)
{
await using var scope = scopeFactory.CreateAsyncScope();
var repo = scope.ServiceProvider.GetRequiredService<IOrderRepository>();
var order = await repo.GetByIdAsync(orderId, ct);
}
}
```text
See [skill:dotnet-csharp-dependency-injection] for lifetime rules, registration patterns, and service scope management.
---
## Slopwatch Anti-Patterns
These are patterns that indicate an agent is hiding problems rather than fixing them. Every code review should check for
these. See [skill:dotnet-slopwatch] for the automated quality gate that detects these patterns.
### 1. Disabled or Skipped Tests
```csharp
[Fact(Skip = "Flaky, will fix later")]
public void CriticalBusinessLogic_WorksCorrectly() { }
#if false
[Fact]
public void ImportantEdgeCase() { }
#endif
```text
**Fix:** Investigate and fix the underlying issue. If a test is genuinely flaky due to timing, use `[Retry]` (xUnit v3)
or fix the non-determinism. Never disable tests to achieve a green build.
### 2. Warning Suppressions
```csharp
#pragma warning disable CS8600, CS8602, CS8604 // suppress all nullability warnings
var result = GetData();
result.Process();
#pragma warning restore CS8600, CS8602, CS8604
```text
**Fix:** Address the underlying nullability or trim issues. Add proper null checks, use nullable annotations correctly,
or apply `[DynamicallyAccessedMembers]` for trim warnings.
### 3. Empty Catch Blocks
```csharp
try
{
await _service.ProcessAsync(data, ct);
}
catch (Exception) { }
catch (Exception ex)
{
}
```text
**Fix:** At minimum, log the exception. Prefer catching specific exception types and handling them appropriately.
### 4. Silenced Analyzers Without Justification
```csharp
[SuppressMessage("Design", "CA1062")]
public void Process(string input) { }
```text
**Fix:** Fix the code to satisfy the analyzer rule, or provide a documented justification in the suppression attribute:
`[SuppressMessage("Design", "CA1062", Justification = "Input validated by middleware")]`.
### 5. Removed Assertions from Tests
```csharp
[Fact]
public async Task CreateOrder_Succeeds()
{
var service = new OrderService();
await service.CreateOrderAsync(new Order());
}
```text
**Fix:** Every test must have at least one assertion that validates the expected behavior. If the test is for side
effects, assert on the side effect (database state, published events, log output).
---
## Cross-References
- [skill:dotnet-csharp-async-patterns] -- async/await deep patterns, `ValueTask`, cancellation
- [skill:dotnet-csharp-dependency-injection] -- DI lifetime rules, registration patterns, scope management
- [skill:dotnet-csharp-nullable-reference-types] -- NRT annotations, nullable context, flow analysis
- [skill:dotnet-csharp-source-generators] -- generator configuration, partial class requirements, diagnostics
- [skill:dotnet-testing-strategy] -- test type decisions, organization, naming conventions
- [skill:dotnet-security-owasp] -- OWASP mitigations, deprecated security API catalog
## References
- [Common .NET Compiler Errors](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/)
- [.NET Trimming Warnings](https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/fixing-warnings)
- [NuGet Package Reference](https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files)
- [Dependency Injection Lifetime Guidelines](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines)