Fixed: Minimal API Accepts an Undefined Enum Value in the Query String
GET /orders?status=99 didn't fail. My enum has three members, and the endpoint happily ran with status 99, which then filters to nothing, with no sign that the request was wrong. .NET's enum parsing allows this on purpose, so the check is yours to add.
What I Was Doing
My .NET C# Web API on ASP.NET Core 10 has a query parameter of an enum type, with three members:
g.MapGet("/enum", (OrderStatus? status) => Results.Ok(new { status }));
GET http://localhost:5190/demo/enum?status=99
What I Expected
A 400, as for any other value that isn't a status.
What I Got
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"status": 99
}

What I Checked
A number that is a member.
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"status": "Fulfilled"
}
So numbers are accepted at all, whether or not they mean anything.
The docs. Enum.TryParse says: "If value is the string representation of an integer that does not represent an underlying value of the TEnum enumeration, the method returns an enumeration member whose underlying value is value converted to an integral type. If this behavior is undesirable, call the IsDefined method to ensure that a particular string representation of an integer is actually a member of TEnum."
Why It Happens
An enum is a number with names on some of its values, and any number of the right type fits. Parsing succeeds, so binding succeeds, and nothing else checks it.
The Fix
Move the query string into a record with [AsParameters], and add EnumDataType, which AddValidation() checks. The property: target matters on an [AsParameters] record, because an attribute on the parameter itself breaks /openapi/v1.json:
/// <summary>The filter and page of the order list, read from the query string.</summary>
public sealed record OrderListQuery(
[property: EnumDataType(typeof(OrderStatus))] OrderStatus? Status = null,
[property: Range(1, 1_000_000)] int Page = 1,
[property: Range(1, 100)] int PageSize = 20);
After the Fix
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": {
"Status": [
"The field Status is invalid."
]
},
"traceId": "00-eab336a5322945321958030bff8e26a2-fc43141c3907b294-00"
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"status": "Placed"
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"status": null
}

A real member still works, and leaving it out still means no filter.
Common Mistakes
| Mistake | What happens | Do this instead |
|---|---|---|
| Assuming a typed parameter means a valid value | 99 gets through | Validate |
Putting [EnumDataType] on the record's parameter without property: | The OpenAPI document breaks | Use [property: ...] |
Checking only Enum.TryParse | Numbers pass it | Add Enum.IsDefined |
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
99 still binds | AddValidation() is missing, or there's no [AsParameters] | Add them |
?status=placed gives a 400 | Query strings use the exact name | The fix |
A JSON body accepts 99 too | The converter allows numbers | new JsonStringEnumConverter(allowIntegerValues: false) |
Conclusion
.NET parses any number into any enum, and minimal API binding uses that parsing. Add EnumDataType to a parameter record, or check Enum.IsDefined, to refuse the values that aren't members.
Additional Resources
- Enum.TryParse — the note about undefined values.
- EnumDataTypeAttribute — the attribute.
