Conversation
A query parameter whose schema is an object and whose style/explode are left at
their defaults — style: form, explode: true — must go on the wire as one
parameter per entry, keyed by the property name alone. The jvm-okhttp client
serialized the whole map with toString() into a single parameter instead
(filter={tld=com, ...}), and a declared map came out as concatenated
Pair.toString()s. The jvm-ktor library already explodes these correctly.
The query building in api.mustache now iterates an exploded map entry by
entry, the same way jvm-ktor does. deepObject and explode: false objects
keep their previous wire format, byte for byte.
The new test fixture covers the four style/explode combinations that decide
the wire format; the test fails without the template change.
No sample changes: no kotlin sample spec declares a free-form or map-typed
query parameter with the default style, the echo api's object query
parameters are all model-typed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CxDNCjqJycKTfzVWg2SeTJ
Contributor
There was a problem hiding this comment.
All reported issues were addressed across 3 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
…y entries Follow-up to the review of the exploded object query parameters in the jvm-okhttp client: - a null map entry is left out instead of going on the wire as the literal "null" (Any?.toString() returns "null" rather than throwing) - a collection value repeats the key once per element instead of being sent as the collection's toString(); scalar values and elements go through parameterToString, so dates get the same format as other query parameters - an exploded entry named like another query parameter no longer overwrites it, or gets overwritten by it: the exploded parameters are now emitted after every declared query parameter and append to an existing entry, so both values end up on the wire To place the exploded parameters after the declared ones, the codegen flags them with x-kotlin-explode-form-object (jvm-okhttp libraries only) and api.mustache emits them in a second pass; the three duplicated required/optional/nullable branches of the first version are gone. deepObject and explode: false parameters are unchanged, and no sample changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Sep 21, 2026
…y loop One comment line above the loop instead of three; the when now only picks the elements and filterNotNull/parameterToString run once. Drop the defensive !isModel check, which no other port has: a map-typed parameter is never isModel. Fix the fixture description, which claimed four style/explode combinations where it covers three, and retitle the test. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ports Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Sep 23, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bug
A query parameter whose schema is an object, with
style/explodeleft at their defaults (form,true), must go on the wire as one parameter per entry, keyed by the property name alone. Thekotlinclient's jvm-okhttp library sent the whole map as a singletoString()parameter instead; kotlin's ownjvm-ktorlibrary gets it right.called with
{"category": "books", "createdDate:gte": "2023-01-01"}:category=books&createdDate%3Agte=2023-01-01filterfilter=%7Bcategory%3Dbooks%2C%20createdDate%3Agte%3D2023-01-01%7D(Map.toString())additionalProperties)typedFilter=%28category%2C%20books%29%28createdDate%3Agte%2C%202023-01-01%29(Pair.toString()s run together)Series
One of six per-language PRs for the same bug: #24797 go, #24802 python, #24803 typescript-fetch, #24867 kotlin, #24868 dart, #24869 ruby. No shared
main/code; each adds the same fixture,3_0/exploded-object-query-param.yaml.Fix
KotlinClientCodegenmarks a query parameter that isisMapandisExplodeand notisDeepObjectwithx-kotlin-explode-form-object(jvm-okhttp libraries only).jvm-okhttp/api.mustacheleaves those out of its per-parameter loop and adds them in a second pass, after every declared query parameter. A free-form object is typedkotlin.Any?, hence a safe cast toMap<*, *>. Per entry:null;tld=com&tld=net);parameterToString, like declared query parameters;Every other parameter produces the line it did before.
Verified
A client generated from the fixture, against a server that echoes its raw query string:
filter(object, defaults)category=books&createdDate%3Agte=2023-01-01typedFilter(map, defaults)category=books&createdDate%3Agte=2023-01-01deepFilter(style: deepObject)deepFilter=%7Bcategory%3Dbooks%2C…%7D(as on master, see Known gaps)flatFilter(explode: false)flatFilter=%7Bcategory%3Dbooks%2C…%7D(as on master, see Known gaps)KotlinClientCodegenApiTest#testExplodedObjectQueryParameterJvmOkhttpfails without the fix. A runtime check on the generated client covers null entries, list, array and empty-list values, aLocalDatevalue, and a colliding name in both orders.Known gaps
deepObjectandexplode: falsestill go out as onetoString()parameter, as on master. Both are wrong per the spec (deepFilter[category]=books,flatFilter=category,books,…); left for a follow-up.filter=<toString()>and is now dropped by the safe cast. A hard cast would throwClassCastExceptioninstead; happy to switch if preferred.PR checklist
./bin/generate-samples.sh bin/configs/kotlin*.yaml): no sample files change, since no kotlin sample spec has a free-form or map-typed query parameter with the default style.Generated with Claude Code