fix: emit repeated query params for all multi-value streaming options#82
Merged
Merged
Conversation
GregHolmes
requested review from
deepgram-kiley and
dg-coreylweathers
as code owners
July 23, 2026 12:45
Generalizes #81, which fixed multi-value keyterm serialization on the streaming connect path. The same bug affects every array-valued query param: the generated streaming clients stringify the value, so a multi-value list collapses into a single param instead of repeated params. The server treats the stringified list as one nonsense value. The prerecorded REST path is unaffected. Route every array-valued streaming param through the shared array-aware serializer the REST path already uses, with repeats enabled, and drop PR #81's bespoke per-term loop. Params fixed on v1 -- keyterm, keywords, replace, search, tag, extra. Params fixed on v2 -- keyterm, tag, language_hint. language_hint on v2 Flux, a string-or-list union structurally identical to keyterm, had the identical bug and is included here. Scalar strings are unchanged; an empty list now omits the param. Both streaming clients are already frozen in .fernignore; comments broadened to cover all multi-value query params. The durable fix belongs upstream in the Fern generator WebSocket template. Closes #77
GregHolmes
force-pushed
the
fix/streaming-multivalue-query-params
branch
from
July 23, 2026 14:45
ab91c3f to
7fb57c0
Compare
dg-coreylweathers
approved these changes
Jul 24, 2026
dg-coreylweathers
left a comment
Contributor
There was a problem hiding this comment.
Verified: serializer behavior, full multi-value param coverage on both listen clients, green build in Docker. LGTM. One non-blocking follow-up on speak v2 tag noted separately.
GregHolmes
added a commit
that referenced
this pull request
Jul 24, 2026
#85) ## Summary Follow-up to #82, extending the same fix to TTS. The speak v2 streaming client mangles a multi-value `tag` exactly as the listen clients did before #82: it serializes the tag union by stringifying the value, so a multi-value list collapses into a single param instead of repeated params: - Wrong: `tag=%5Ba%2C+b%5D` — one param, value "[a, b]" - Right: `tag=a&tag=b` — one param per value ## Root cause Same class as the listen fix. The Fern generator hand-rolls the streaming query-building path and stringifies array params, while the REST path routes through the shared array-aware serializer and is correct. This is the same generator defect in the WebSocket client template. ## Fix Route `tag` through the shared array-aware serializer with repeats enabled, matching the listen fix and the REST path. `tag` is the only array-valued query param on speak v2. Scalar strings are unchanged; an empty list now omits the param. ## Freeze / regen The speak v2 client is already frozen in `.fernignore`; comments updated so both the dispatcher patch and this multi-value fix are documented against it. The durable fix belongs upstream in the Fern generator WebSocket template, so listen and speak get cured together. ## Tests New connect-handshake wire coverage with MockWebServer, asserting the captured `/v2/speak` upgrade URL: a multi-value list becomes repeated params, a single string stays one param. Also verified with a throwaway matrix across four shapes -- multi-list, single-list, scalar, empty -- all passing. `./gradlew spotlessJavaCheck test` is green. Credit to Corey for spotting this in the #82 review.
GregHolmes
pushed a commit
that referenced
this pull request
Jul 24, 2026
🤖 I have created a release *beep* *boop* --- ## [0.7.1](v0.7.0...v0.7.1) (2026-07-24) ### Bug Fixes * emit additionalProperties as query params on streaming connect ([#86](#86)) ([28229fc](28229fc)) * emit repeated query params for all multi-value streaming options ([#82](#82)) ([746be15](746be15)), closes [#77](#77) * emit repeated tag query params for multi-value speak v2 streaming ([#85](#85)) ([e748619](e748619)) #### What's in this release Three related fixes to streaming (WebSocket) query-parameter serialization on `listen` and `speak`. All are bug fixes — no API changes. **Multi-keyterm streaming fix** - Streaming connections mis-serialized a multi-value `keyterm` (and other array-valued params): a `List<String>` was stringified into a single param (`keyterm=[a, b]`) instead of repeated params (`keyterm=a&keyterm=b`). The server treats the stringified list as one nonsense term, so multiple key terms were not boosted — recognition of them was actually degraded. - Now fixed for every array-valued streaming query param — `listen`: `keyterm`, `keywords`, `replace`, `search`, `tag`, `extra`, `language_hint`; `speak`: `tag` — by routing them through the same repeated-param serialization the REST path already uses (#81, #82, #85; closes #77). - **If you worked around this by hand-joining terms into a single string, drop that workaround** — pass a real `List<String>` (e.g. `ListenV1Keyterm.of(List.of("a", "b"))`) and each term is now sent as its own `keyterm=` param. **Unmodeled streaming query params now work (e.g. `no_delay`)** - The streaming `connect(...)` builders dropped `additionalProperties`, so unmodeled params set via `.additionalProperty("no_delay", true)` never reached the URL — it was impossible to set `no_delay` on a streaming connection. - Now fixed on all four connect clients (`listen` v1/v2, `speak` v1/v2): `additionalProperties` are emitted as query params, so any not-yet-modeled param can be passed through until it gains a typed option (#86; closes #83). _All four streaming clients remain frozen in `.fernignore`; the durable fix belongs in the Fern generator's WebSocket template, tracked for the next generator upgrade._ --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.
Summary
Generalizes #81. That PR fixed multi-value
keytermserialization on the streaming connect path, but the same bug affects every array-valued query param. The generated streaming clients serialize each param by stringifying the value, so a multi-valueList<String>collapses into a single param instead of repeated params:keyterm=%5Ba%2C+b%5D— one param, value "[a, b]"keyterm=a&keyterm=b— one param per valueThe server treats the stringified list as a single nonsense value. The prerecorded REST path is unaffected — it already routes query params through the shared array-aware serializer with repeats enabled.
Root cause
Not a spec problem. The same Fern generator produces two query-building paths from the same spec. The REST path uses the shared array-aware serializer and is correct. The streaming/WebSocket path is hand-rolled and stringifies lists, so it mangles them. This is a generator defect in the WebSocket client template.
Fix
Route every array-valued streaming param through the same shared serializer the REST path uses, with array-as-repeats enabled, and drop PR #81's bespoke per-term loop. Params fixed:
/v1/listen: keyterm, keywords, replace, search, tag, extra/v2/listen: keyterm, tag, language_hintlanguage_hinton v2 Flux — aString | List<String>union structurally identical tokeyterm— had the identical bug and is included here.Behavior
Freeze / regen
Both streaming clients are already frozen in
.fernignore; comments broadened from "multi-keyterm" to "multi-value query param". The durable fix belongs upstream in the Fern generator's WebSocket template, tracked so these entries can be unfrozen once it lands.Tests
Extended connect-handshake wire coverage with MockWebServer, asserting the captured upgrade URL for every array param on both clients, plus scalar-string guards.
./gradlew spotlessJavaCheck testis green. Also verified with a throwaway matrix across 9 params and 4 shapes — multi-list, single-list, scalar, empty — plus exact raw-query and percent-encoding assertions, all passing.Closes #77