Fixed: "JSON deserialization for type was missing required properties" in an ASP.NET Core Minimal API
A POST with an empty JSON body answered 400 Bad Request with no explanation, and the log said JSON deserialization for type 'Dewiride.Inventory.Data.Entities.Category' was missing required properties including: 'name'. The C# required keyword on my entity was doing its job, too early and too quietly. A request record with validation attributes says the same thing in a way a client can read.
What I Was Doing
In a .NET 10 minimal API, I bound the request body directly to an EF Core entity, whose Name uses C#'s required keyword:
public required string Name { get; set; }
app.MapPost("/demo/bind-entity", async (Category category, InventoryDbContext db) =>
{
db.Categories.Add(category);
await db.SaveChangesAsync();
return Results.Ok(category.Id);
});
Then I sent a body with no name:
POST http://localhost:5190/demo/bind-entity
Content-Type: application/json
{}
What I Expected
A 400 that says the name is required.
What I Got
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"traceId": "00-e8644abac20aa438db25bcff061ebf07-b13b5ae3ced9a6fb-00"
}
fail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[1]
An unhandled exception has occurred while executing the request.
Microsoft.AspNetCore.Http.BadHttpRequestException: Failed to read parameter "Category category" from the request body as JSON.
---> System.Text.Json.JsonException: JSON deserialization for type 'Dewiride.Inventory.Data.Entities.Category' was missing required properties including: 'name'.
So the status was right, but the client only sees "title": "Bad Request", and the reason stays in the log.

What I Checked
Who checks required. System.Text.Json does. Its required properties page lists the required modifier as one of the ways to mark a property that "must be present in the JSON payload", and says deserialization throws when it's missing. That happens while the body is being read, before validation or your endpoint run.
Why the client sees nothing. A failure while reading the body becomes BadHttpRequestException, with no per-field details. In Development the message is in the log. In Production, ASP.NET Core answers 400 directly, without throwing, as the error handling page describes.
What else binding an entity allows. With a name, the same endpoint let me choose the row's Id, which should be MySQL's decision:
POST http://localhost:5190/demo/bind-entity
Content-Type: application/json
{
"id": 500,
"name": "Chosen id",
"createdAtUtc": "2001-01-01T00:00:00Z"
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
500
So this wasn't only a question of the error message.
Why It Happens
The required keyword belongs to the C# class: it says code can't create the object without setting this property. System.Text.Json respects that when it creates the object from JSON, and stops with an exception when the property isn't there. It's a correct check at the wrong layer for API clients: reading stops at the first problem, and nothing turns it into a message for them.
The Fix
Bind a request record instead of the entity, and validate it. The record holds only what a client may send:
public sealed record CategoryRequest(
[Required, StringLength(100)] string Name);
The [Required] here is a validation attribute, from System.ComponentModel.DataAnnotations, not the C# keyword. With builder.Services.AddValidation(); in Program.cs, it runs after the JSON is read, and reports every field that fails. A missing Name simply arrives as null, because a positional record has no required keyword to trip over.
The endpoint then builds the entity itself:
var category = new Category
{
Name = request.Name.Trim(),
CreatedAtUtc = clock.GetUtcNow().UtcDateTime,
};
After the Fix
The same empty body, against the endpoint that binds the record:
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"Name": [
"The Name field is required."
]
},
"traceId": "00-5071d49a80c928428ba5a6f9b48f082c-ea693eb6dccabb12-00"
}

The client can now see which field is wrong, and the client can't choose an Id or a creation date anymore, because the record has no such properties.
Common Mistakes
| Mistake | What happens | Do this instead |
|---|---|---|
| Binding EF Core entities to request bodies | Clients can set any column, and errors say little | Bind a request record |
Removing required from the entity to stop the error | The compiler stops protecting code that creates entities | Keep it on the entity |
Using [JsonRequired] on request records | The same bare 400 | Use validation attributes |
Confusing required with [Required] | One's checked while reading JSON, the other during validation | Use [Required] on requests |
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
was missing required properties including: 'name' | A required property isn't in the JSON | Bind a record with [Required] |
A bare 400 with no errors list | The body couldn't be read as that type | Check the Development log for the reason |
[Required] has no effect | AddValidation isn't registered | Add it to Program.cs |
| The property exists in the JSON, and it's still reported missing | The name doesn't match the C# property | Check the spelling: case doesn't matter by default, other differences do |
FAQ
Is required useless on entities, then?
No. It's useful there: any code that creates a Category must set Name, and the compiler checks that. It just shouldn't be what validates client input.
Does the positional record's non-nullable string protect me?
Not by itself. JSON without the property leaves it null, and only [Required] catches that. That's why the record has both.
Conclusion
"JSON deserialization for type was missing required properties" comes from the C# required keyword, checked while reading the body. It stops the request, but clients get a bare 400, and binding an entity lets them set other columns too. Bind a request record with validation attributes, and let the endpoint build the entity.
Additional Resources
- Required properties in System.Text.Json — the three ways to mark one.
- Validation in Minimal APIs —
AddValidationand attributes.
