BadHttpRequestException: Failed to read parameter from the request body as JSON.
ID: dotnet/minimal-api-binding
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| 8 | active | — | — | — |
Root Cause
Minimal API endpoints in ASP.NET Core automatically bind complex types from the request body using System.Text.Json. This exception occurs when the request body is missing, malformed, has incorrect Content-Type, or when the JSON structure does not match the expected parameter type. Common causes include sending form data instead of JSON, missing required properties when using required keyword or [Required] attributes, or enum/type mismatches. Fix by ensuring the request Content-Type is application/json and the body structure matches the expected type.
genericWorkarounds
-
92% success Ensure the request sends application/json Content-Type with a matching JSON body
The most common cause is a missing or incorrect Content-Type header. Verify the client sends Content-Type: application/json and the body is valid JSON matching the parameter type. For testing: curl -X POST https://localhost:5001/api/items -H "Content-Type: application/json" -d '{"name": "test", "price": 9.99}'. When using HttpClient: content = new StringContent(json, Encoding.UTF8, "application/json"). Form-encoded data will not bind to JSON parameters -
87% success Configure JsonSerializerOptions to handle common mismatches like camelCase and enums
Configure the JSON serializer to be more lenient with common mismatches: builder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.PropertyNameCaseInsensitive = true; options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()); options.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; }); PropertyNameCaseInsensitive handles PascalCase vs camelCase mismatches, and JsonStringEnumConverter allows string enum values instead of requiring integers -
84% success Use a custom binding approach with JsonDocument for flexible body handling
When the request body structure varies or needs validation before binding, accept the raw body and parse manually: app.MapPost("/api/items", async (HttpRequest request) => { using var doc = await JsonDocument.ParseAsync(request.Body); var root = doc.RootElement; if (!root.TryGetProperty("name", out var name)) return Results.BadRequest("name is required"); var item = new Item { Name = name.GetString()! }; return Results.Ok(item); }); This gives complete control over parsing and error messages. For simpler cases, use the JsonElement type as parameter for partial flexibility
Dead Ends
Common approaches that don't work:
-
Adding [FromBody] attribute explicitly to the parameter assuming it changes binding behavior
85% fail
In Minimal APIs, complex types are already bound from the body by default. Adding [FromBody] explicitly does not change the binding source or fix deserialization errors. The issue is in the JSON payload structure or Content-Type header, not in how the parameter is annotated. This wastes debugging time on the wrong layer
-
Making all properties nullable to avoid required property binding failures
75% fail
Making all properties nullable suppresses the binding error but pushes null-handling responsibility throughout the entire codebase. Every consumer of the model must now check for null on every field, and invalid requests that should be rejected with 400 are silently accepted with null data, leading to NullReferenceExceptions deeper in the application logic
-
Switching from Minimal API to MVC controllers to get better model binding
80% fail
MVC controllers use the same System.Text.Json deserialization pipeline and will produce equivalent errors for malformed JSON. The migration adds significant complexity (controller classes, routing attributes, dependency injection changes) without addressing the root cause, which is a mismatch between the client request and the server's expected model