What are you generating using Kiota, clients or plugins?
API Client/SDK
In what context or format are you using Kiota?
Nuget tool (installed as a dotnet tool)
Client library/SDK language
Csharp
Describe the bug
For a query parameter of type: array, Kiota generates a URI template that comma-joins the array values (?ids=1,2,3) instead of repeating the key (?ids=1&ids=2&ids=3) whenever the parameter is marked required: true in the OpenAPI document — regardless of what style/explode is set on that parameter.
The exact same array parameter, only marked required: false, is generated correctly with the explode modifier and serializes as repeated keys at runtime.
Concretely, across three otherwise-identical operations differing only in required/style/explode on the array parameter, Kiota generates:
| parameter definition |
generated request builder base template |
required: true, no style/explode set |
{+baseurl}/required-array?ids={ids} |
required: true, style: form, explode: true set explicitly |
{+baseurl}/required-array-explicit-explode?ids={ids} (identical to the row above — explode is ignored) |
required: false, no style/explode set |
{+baseurl}/optional-array{?ids*} (correct) |
And the resulting runtime request URIs (setting Ids = [1, 2, 3] on each generated query-parameters class) are:
required, no explicit style/explode : https://example.org/required-array?ids=1,2,3
required, explicit style:form/explode:true : https://example.org/required-array-explicit-explode?ids=1,2,3
optional, no explicit style/explode : https://example.org/optional-array?ids=1&ids=2&ids=3
This breaks any API where an array query parameter is required and the server expects the standard OpenAPI 3 default query serialization (style: form, explode: true, i.e. repeated keys) — which is the default per the OpenAPI 3.0 spec for in: query parameters, and is what ASP.NET Core's default model binding (and presumably most other frameworks) expects for T[]/List<T>-bound query parameters. Kiota-generated clients silently send a malformed query string and the server rejects the request (in our case, an ASP.NET Core API returned a 400 validation error), with no warning at generation time that the required: true + array combination degrades the parameter's serialization style.
We hit this on a real endpoint (GET /api/v1/{companyId}/restaurants/planner/monthlysummary?Month=...&restaurantIds=...) where restaurantIds is a required array of integer. The sibling endpoint GET /api/v1/{companyId}/pointofsale/tables has an optional array parameter (RestaurantIds) of the same shape and generates correctly ({?OnlyOpenTables*,RestaurantIds*}).
Expected behavior
An array-typed query parameter should generate a URI template with the explode modifier ({?ids*} grouping, or equivalent) whenever style: form/explode: true applies (the OpenAPI 3.0 default, or explicitly declared) — independent of whether the parameter is required. required should only affect whether the SDK enforces the value being supplied, not how the value is serialized into the query string.
How to reproduce
- Save the OpenAPI description below as
repro.yaml.
- Run:
kiota generate --language csharp --openapi repro.yaml --output kiota-out --class-name ReproClient --namespace-name ReproSDK --clean-output
- Inspect the generated
*RequestBuilder.cs constructors for each of the three operations — see the base(requestAdapter, "...", ...) call's URL template string.
- Optionally, build a tiny console app referencing the generated code +
Microsoft.Kiota.Http.HttpClientLibrary (1.22.2) and print ToGetRequestInformation(cfg => cfg.QueryParameters.Ids = new int?[] { 1, 2, 3 }).URI for each operation to see the actual malformed vs. correct runtime URIs shown above.
openapi: 3.0.1
info:
title: repro
version: "1.0"
servers:
- url: https://example.org
paths:
/required-array:
get:
operationId: getRequiredArray
parameters:
- name: ids
in: query
required: true
schema:
type: array
items:
type: integer
responses:
"200":
description: ok
/required-array-explicit-explode:
get:
operationId: getRequiredArrayExplicitExplode
parameters:
- name: ids
in: query
required: true
style: form
explode: true
schema:
type: array
items:
type: integer
responses:
"200":
description: ok
/optional-array:
get:
operationId: getOptionalArray
parameters:
- name: ids
in: query
required: false
schema:
type: array
items:
type: integer
responses:
"200":
description: ok
Open API description file
Inline above (also attaching repro.yaml).
Kiota Version
1.34.1+9f9cfb3b1cb9b5311a214ea6ce0f69943c523005
Latest Kiota version known to work for scenario above?(Not required)
Not tested against earlier versions — the fact that required and optional array parameters go through visibly different template-construction branches suggests this may not be a recent regression, but we haven't bisected it.
Known Workarounds
- Server-side (OpenAPI doc): declare the array parameter as
required: false even if it's functionally required — enforce "must be provided" via the API's own validation layer instead of the OpenAPI required flag. This is enough to make Kiota emit the correct exploded template. (Downside: the generated client's documentation/nullability annotations no longer reflect that the parameter is actually mandatory.)
- Client-side: bypass the generated request builder's query-parameter binding entirely by constructing the query string manually and calling the generated
WithUrl(rawUrl) method on the request builder, which accepts an arbitrary pre-built URL and skips the broken template.
Configuration
- OS: macOS 26.3.1 (Darwin 25.3.0)
- architecture: arm64 (Apple Silicon)
- Kiota installed via
dotnet tool install --global Microsoft.OpenApi.Kiota
- .NET SDK: 10.0.302
- Not verified whether other target languages (Go, Java, TypeScript, Python, etc.) exhibit the same behavior — we only tested
--language csharp.
Other information
The divergence appears to be in how Kiota's URI-template builder decides where to place a query parameter: parameters that are optional get grouped into a reserved-expansion block ({?a*,b*}) that preserves the explode modifier per-parameter, while parameters marked required: true get inlined directly into the base template as plain name={name} simple-string expansion — which has no concept of "explode" for arrays and falls back to comma-joining. This branch looks like it does not consult the parameter's style/explode schema properties at all once a parameter is required, which is why setting them explicitly makes no difference (verified above).
Possibly related (reports comma-joined arrays, but doesn't identify required as the trigger): #6488.
What are you generating using Kiota, clients or plugins?
API Client/SDK
In what context or format are you using Kiota?
Nuget tool (installed as a
dotnet tool)Client library/SDK language
Csharp
Describe the bug
For a
queryparameter oftype: array, Kiota generates a URI template that comma-joins the array values (?ids=1,2,3) instead of repeating the key (?ids=1&ids=2&ids=3) whenever the parameter is markedrequired: truein the OpenAPI document — regardless of whatstyle/explodeis set on that parameter.The exact same array parameter, only marked
required: false, is generated correctly with the explode modifier and serializes as repeated keys at runtime.Concretely, across three otherwise-identical operations differing only in
required/style/explodeon the array parameter, Kiota generates:required: true, nostyle/explodeset{+baseurl}/required-array?ids={ids}required: true,style: form,explode: trueset explicitly{+baseurl}/required-array-explicit-explode?ids={ids}(identical to the row above — explode is ignored)required: false, nostyle/explodeset{+baseurl}/optional-array{?ids*}(correct)And the resulting runtime request URIs (setting
Ids = [1, 2, 3]on each generated query-parameters class) are:This breaks any API where an array query parameter is required and the server expects the standard OpenAPI 3 default query serialization (
style: form,explode: true, i.e. repeated keys) — which is the default per the OpenAPI 3.0 spec forin: queryparameters, and is what ASP.NET Core's default model binding (and presumably most other frameworks) expects forT[]/List<T>-bound query parameters. Kiota-generated clients silently send a malformed query string and the server rejects the request (in our case, an ASP.NET Core API returned a 400 validation error), with no warning at generation time that therequired: true+ array combination degrades the parameter's serialization style.We hit this on a real endpoint (
GET /api/v1/{companyId}/restaurants/planner/monthlysummary?Month=...&restaurantIds=...) whererestaurantIdsis a requiredarrayofinteger. The sibling endpointGET /api/v1/{companyId}/pointofsale/tableshas an optional array parameter (RestaurantIds) of the same shape and generates correctly ({?OnlyOpenTables*,RestaurantIds*}).Expected behavior
An array-typed
queryparameter should generate a URI template with the explode modifier ({?ids*}grouping, or equivalent) wheneverstyle: form/explode: trueapplies (the OpenAPI 3.0 default, or explicitly declared) — independent of whether the parameter isrequired.requiredshould only affect whether the SDK enforces the value being supplied, not how the value is serialized into the query string.How to reproduce
repro.yaml.*RequestBuilder.csconstructors for each of the three operations — see thebase(requestAdapter, "...", ...)call's URL template string.Microsoft.Kiota.Http.HttpClientLibrary(1.22.2) and printToGetRequestInformation(cfg => cfg.QueryParameters.Ids = new int?[] { 1, 2, 3 }).URIfor each operation to see the actual malformed vs. correct runtime URIs shown above.Open API description file
Inline above (also attaching
repro.yaml).Kiota Version
1.34.1+9f9cfb3b1cb9b5311a214ea6ce0f69943c523005
Latest Kiota version known to work for scenario above?(Not required)
Not tested against earlier versions — the fact that
requiredandoptionalarray parameters go through visibly different template-construction branches suggests this may not be a recent regression, but we haven't bisected it.Known Workarounds
required: falseeven if it's functionally required — enforce "must be provided" via the API's own validation layer instead of the OpenAPIrequiredflag. This is enough to make Kiota emit the correct exploded template. (Downside: the generated client's documentation/nullability annotations no longer reflect that the parameter is actually mandatory.)WithUrl(rawUrl)method on the request builder, which accepts an arbitrary pre-built URL and skips the broken template.Configuration
dotnet tool install --global Microsoft.OpenApi.Kiota--language csharp.Other information
The divergence appears to be in how Kiota's URI-template builder decides where to place a query parameter: parameters that are optional get grouped into a reserved-expansion block (
{?a*,b*}) that preserves the explode modifier per-parameter, while parameters markedrequired: trueget inlined directly into the base template as plainname={name}simple-string expansion — which has no concept of "explode" for arrays and falls back to comma-joining. This branch looks like it does not consult the parameter'sstyle/explodeschema properties at all once a parameter is required, which is why setting them explicitly makes no difference (verified above).Possibly related (reports comma-joined arrays, but doesn't identify
requiredas the trigger): #6488.