Skip to main content

Fixed: "JSON deserialization for type was missing required properties" in an ASP.NET Core Minimal API

· 6 min read
Jagdish Kumawat
Founder @ Dewiride

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:

src/Dewiride.Inventory.Data/Entities/Category.cs (excerpt)
public required string Name { get; set; }
src/Dewiride.Inventory.Api/Program.cs (temporary endpoint)
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:

Request
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​

Response
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"
}
API log (the lines that matter)
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.

A terminal with a POST with an empty JSON body answered with 400 Bad Request and no details, and the API log with JsonException: JSON deserialization for type Category was missing required properties including: 'name'.

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:

Request
POST http://localhost:5190/demo/bind-entity
Content-Type: application/json

{
"id": 500,
"name": "Chosen id",
"createdAtUtc": "2001-01-01T00:00:00Z"
}
Response
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:

src/Dewiride.Inventory.Api/Features/Categories/CategoryContracts.cs (excerpt)
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:

src/Dewiride.Inventory.Api/Features/Categories/CategoryEndpoints.cs (excerpt)
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:

Response
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"
}

A terminal with a 400 Bad Request response to the same empty body, with a problem details body whose errors list says Name: The Name field is required.

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​

MistakeWhat happensDo this instead
Binding EF Core entities to request bodiesClients can set any column, and errors say littleBind a request record
Removing required from the entity to stop the errorThe compiler stops protecting code that creates entitiesKeep it on the entity
Using [JsonRequired] on request recordsThe same bare 400Use validation attributes
Confusing required with [Required]One's checked while reading JSON, the other during validationUse [Required] on requests

Troubleshooting​

SymptomLikely causeFix
was missing required properties including: 'name'A required property isn't in the JSONBind a record with [Required]
A bare 400 with no errors listThe body couldn't be read as that typeCheck the Development log for the reason
[Required] has no effectAddValidation isn't registeredAdd it to Program.cs
The property exists in the JSON, and it's still reported missingThe name doesn't match the C# propertyCheck 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​

Stay Updated

Subscribe to our newsletter for the latest tutorials, tech insights, and developer news.

By subscribing, you agree to our privacy policy. Unsubscribe at any time.