Skip to main content

Fixed: "Failed to bind parameter" for an Enum in a Minimal API Query String

· 5 min read
Jagdish Kumawat
Founder @ Dewiride

?status=Placed worked, and ?status=placed gave a 400 Bad Request with no explanation. The reason was only in the log: Failed to bind parameter "Nullable<OrderStatus> status" from "placed". JSON bodies accept any case here, and query strings don't.

What I Was Doing​

My .NET C# Web API on ASP.NET Core 10 has an endpoint with an optional enum in the query string:

An endpoint
g.MapGet("/enum", (OrderStatus? status) => Results.Ok(new { status }));

I called it with the status in lower case:

Request
GET http://localhost:5190/demo/enum?status=placed

What I Expected​

The same result as ?status=Placed, since JsonStringEnumConverter accepts any case in a JSON body.

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-3b7e42e64172e7638ebbf246dc598e78-11c712304ca311cf-00"
}
API log
Microsoft.AspNetCore.Http.BadHttpRequestException: Failed to bind parameter "Nullable<OrderStatus> status" from "placed".

A response of 400 Bad Request with no details, and the log line BadHttpRequestException: Failed to bind parameter "Nullable OrderStatus status" from "placed".

What I Checked​

The exact name, and a wrong name.

Response (?status=Placed)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
"status": "Placed"
}
API log (?status=Nope)
Microsoft.AspNetCore.Http.BadHttpRequestException: Failed to bind parameter "Nullable<OrderStatus> status" from "Nope".

So it's strict about case, and it treats a wrong case like a wrong word.

The same value in a JSON body:

Response (a POST with the type in lower case)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
"type": "Receipt",
"status": null
}

The docs. The parameter binding page has this exact failure for a number: /products?pageNumber=two gives BadHttpRequestException: Failed to bind parameter "Nullable<int> pageNumber" from "two". Query values go through TryParse, and JSON options don't apply to them.

Why It Happens​

For a query string, the binding code parses the text as the enum, with the exact name, and it doesn't read your JSON settings. When parsing fails, the endpoint doesn't run. The response is a plain 400, and the reason goes to the log.

The Fix​

If the exact name is good enough, nothing needs fixing, and your API documentation can list the values. When you want any case, and a message that helps the client, accept a string and parse it yourself:

An endpoint
g.MapGet("/enum-string", (string? status) =>
{
if (status is null)
{
return Results.Ok(new { status = (OrderStatus?)null });
}

if (!Enum.TryParse<OrderStatus>(status, ignoreCase: true, out var parsed) || !Enum.IsDefined(parsed))
{
return Results.ValidationProblem(new Dictionary<string, string[]>
{
["status"] = [$"'{status}' isn't a status. Use one of: {string.Join(", ", Enum.GetNames<OrderStatus>())}."],
});
}

return Results.Ok(new { status = (OrderStatus?)parsed });
});

ignoreCase: true allows any case, and Enum.IsDefined refuses numbers that aren't members.

After the Fix​

Response (?status=placed)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
"status": "Placed"
}
Response (?status=Nope)
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": [
"'Nope' isn't a status. Use one of: Placed, Fulfilled, Cancelled."
]
},
"traceId": "00-8ba9062f0ba8794c433afe39b0599423-005d9387523e7245-00"
}

A response of 200 for status=placed, and a 400 for status=Nope that lists the allowed values.

Common Mistakes​

MistakeWhat happensDo this instead
Adding JsonStringEnumConverter to fix itIt only affects JSONParse the string
Assuming the 400 came from your validationThe endpoint never ranRead the log
Dropping Enum.IsDefined?status=99 parsesKeep both checks

Troubleshooting​

SymptomLikely causeFix
Required parameter ... was not providedThe parameter isn't nullableThe header version of this
?status=1 worksNumbers parse as enumsThe fix
The 400 has no body in productionProblem details aren't set upAdd AddProblemDetails()

Conclusion​

Query strings parse enums by exact name, whatever your JSON settings say. If that's too strict, take a string, parse it with ignoreCase: true, and answer with a message that lists the valid values.

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.