Fixed: "InvalidCastException: Unable to cast object of type 'System.Attribute[]' to type 'IEnumerable`1[ValidationAttribute]'" with AsParameters in /openapi/v1.json
The API itself worked, but /openapi/v1.json started answering 500: InvalidCastException: Unable to cast object of type 'System.Attribute[]' to type 'System.Collections.Generic.IEnumerable1[System.ComponentModel.DataAnnotations.ValidationAttribute]'.The cause was a record used with[AsParameters], with validation attributes on its constructor parameters. It's a bug in ASP.NET Core 10, and adding property:` to each attribute avoids it.
What I Was Doing
I was adding a product list to a .NET 10 minimal API. Its options come from the query string, so I put them in a record and bound it with [AsParameters], with validation attributes on the page size and sort order:
public sealed record ProductListQuery(
string? Search,
int? CategoryId,
bool IncludeArchived = false,
[AllowedValues("name", "-name", "price", "-price", "newest", null)] string? Sort = null,
[Range(1, 1_000_000)] int Page = 1,
[Range(1, 100)] int PageSize = 20);
private static async Task<Ok<PagedResponse<ProductResponse>>> ListAsync(
[AsParameters] ProductListQuery query,
The endpoint and its validation worked. ?pageSize=1000 answered 400, and a normal request answered 200. Then I opened the OpenAPI document, which MapOpenApi serves in Development:
GET http://localhost:5190/openapi/v1.json
What I Expected
200 OK, with the JSON description of every endpoint, including the new query parameters.
What I Got
HTTP/1.1 500 Internal Server Error
Content-Type: application/problem+json
{"type":"https://tools.ietf.org/html/rfc9110#section-15.6.1","title":"An error occurred while processing your request.","status":500,"traceId":"00-796e7860ee780e91007206353b7f162d-74850200b3154561-00"}
fail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[1]
An unhandled exception has occurred while executing the request.
System.InvalidCastException: Unable to cast object of type 'System.Attribute[]' to type 'System.Collections.Generic.IEnumerable`1[System.ComponentModel.DataAnnotations.ValidationAttribute]'.
at System.Reflection.CustomAttributeExtensions.GetCustomAttributes[T](ParameterInfo element)
at Microsoft.AspNetCore.OpenApi.JsonNodeSchemaExtensions.ApplyParameterInfo(JsonNode schema, ApiParameterDescription parameterDescription, JsonTypeInfo jsonTypeInfo)
at Microsoft.AspNetCore.OpenApi.OpenApiSchemaService.GetOrCreateUnresolvedSchemaAsync(OpenApiDocument document, Type type, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, ApiParameterDescription parameterDescription, CancellationToken cancellationToken)
![A terminal where GET /openapi/v1.json answers 500, and the API log shows System.InvalidCastException: Unable to cast object of type 'System.Attribute[]', thrown from JsonNodeSchemaExtensions.ApplyParameterInfo.](https://strdewiridedevblogsprod.blob.core.windows.net/images/blog/programming/csharp/fixes/2026/openapi-cast-01-error.webp)
What I Checked
Which change caused it. ApplyParameterInfo is in the stack trace, so it was about a parameter's attributes. The only new parameters with attributes were the ones in ProductListQuery. The body records with validation attributes, such as CreateProductRequest, were fine.
Whether it's known. It is: dotnet/aspnetcore issue #65348, "InvalidCastException when using [AsParameters] with primary constructor record that has ValidationAttribute on constructor parameters". It has the same stack trace, and a four-line way to reproduce it.
Where the bug is. A comment on that issue points at PropertyAsParameterInfo, the class that presents a record's property as a parameter. When a constructor parameter has attributes, the ASP.NET Core 10 source merges them into an array of the wrong type:
var mergedAttributes = new Attribute[constructorAttributes.Length + propertyAttributes.Length];
GetCustomAttributes<T> then tries to treat that Attribute[] as a list of ValidationAttribute, and the cast fails.
Whether it's fixed. Pull request #67284 fixed it for 11.0-preview7. The release/10.0 branch above still has the old code, and this API runs on 10.0.14, because .NET 10 is the newest generally available release.
Why It Happens
In a record's primary constructor, an attribute goes on the constructor parameter by default, not on the property the compiler generates. The code above shows the bug needs constructor attributes: with none, GetCustomAttributes returns the property's attributes directly, in an array of the right type. So moving the attributes to the property avoids the broken path.
The Fix
Target the property, with property: in front of each attribute:
public sealed record ProductListQuery(
string? Search,
int? CategoryId,
bool IncludeArchived = false,
[property: AllowedValues("name", "-name", "price", "-price", "newest", null, ErrorMessage = "Sort must be name, -name, price, -price or newest.")]
string? Sort = null,
[property: Range(1, 1_000_000)] int Page = 1,
[property: Range(1, 100)] int PageSize = 20);
Only records used with [AsParameters] need this. Records bound from the JSON body can keep their attributes as they are.
After the Fix
200 OK
/api/v1/categories post, get
/api/v1/categories/{id} get, put, delete
/api/v1/products post, get
/api/v1/products/{id} get, put
/api/v1/products/{id}/archive post
/api/v1/products/{id}/restore post
And validation still works, which was the thing to check:
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": {
"PageSize": [
"The field PageSize must be between 1 and 100."
]
},
"traceId": "00-d83d3bde5633b55ce46b08a481e27c3c-71fb45f1a5726033-00"
}

Common Mistakes
| Mistake | What happens | Do this instead |
|---|---|---|
| Only testing the endpoints themselves | The document can be broken while the API works | Open /openapi/v1.json after adding parameters |
| Removing the validation attributes to make the error go away | Clients can ask for a million rows | Use property: |
| Switching to Swagger or another generator for this | A lot of change for one prefix | Use property: |
Assuming property: turns validation off | It doesn't: it still answered 400 here | Test one bad value to be sure |
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
/openapi/v1.json answers 500 with this InvalidCastException | Validation attributes on the constructor parameters of an [AsParameters] record | Add property: |
| Scalar or Swagger UI shows an error or an empty page | The document they read answers 500 | The same; check the JSON address directly |
The cast error names DescriptionAttribute | The same bug, with another attribute on an [AsParameters] record | The same |
| Validation stops working | The attribute targets something else, such as param: | Use property: |
FAQ
Do I need this on .NET 11?
The fix was merged for 11.0-preview7, so it shouldn't be needed there. property: keeps working anyway.
Why does a body record not need it?
The bug is in PropertyAsParameterInfo, which only [AsParameters] uses.
Conclusion
Unable to cast object of type 'System.Attribute[]' in /openapi/v1.json comes from a bug in ASP.NET Core 10, triggered by validation attributes on the constructor parameters of an [AsParameters] record. Add property: to those attributes. The document generates again, and validation still runs.
Additional Resources
- dotnet/aspnetcore issue #65348 — the bug report.
- Pull request #67284 — the fix, for .NET 11.
- Parameter binding: AsParameters — how
[AsParameters]works.
