Skip to content

fix: send list-of-primitive multipart fields as repeated form fields (#819) - #825

Merged
kraenhansen merged 1 commit into
mainfrom
kh/fix-819-multipart-list-serialization
Jul 30, 2026
Merged

fix: send list-of-primitive multipart fields as repeated form fields (#819)#825
kraenhansen merged 1 commit into
mainfrom
kh/fix-819-multipart-list-serialization

Conversation

@kraenhansen

@kraenhansen kraenhansen commented Jul 29, 2026

Copy link
Copy Markdown
Member

Summary

Interim fix for #819. speech_to_text.convert(keyterms=[...]) (and other multipart list fields) fails with invalid_keyword_length because the whole list is sent as one JSON-array string instead of repeated form fields.

Root cause (verified): generator fern-python-sdk 4.46.9 (commit cb4feb824, PR fern-api/fern#11284, 2026-01-08) added an _is_primitive_type() gate to multipart serialization: only scalar primitives are sent directly, everything else — including List[str] — is json.dumps(jsonable_encoder(...))'d. PR #817 bumped us onto that behavior (4.42.0 → 4.64.1). The change was undocumented — it shipped bundled inside a "fix python wire tests" PR. Fixed upstream in generator 5.22.2, but that's past the 5.5.4 min-Python-3.10 clamp, so we can't just upgrade forward.

⚠️ This is a temporary patch — when to revert

This hand-edits Fern-generated files, so it must be retired once the generator itself emits the correct code. Revert (via fern replay forget <patch-id>, then regenerate) as soon as either of these is true:

  • The generator is upgraded to ≥ 5.22.2 — the version that fixes list-of-primitive multipart serialization (fix-multipart-list-of-primitives-repeated-fields.yml). ⚠️ Note: ≥ 5.5.4 raises the generated min-Python to 3.10 — only take this path once we're ready for that breaking change.
  • A 4.x backport of the 5.22.2 fix lands (requested from Fern) and we pin to it — keeps Python 3.8 support and fixes the bug, at which point this patch is fully redundant.

Until then, Fern Replay re-applies this patch on every regeneration. Leaving it in place after the generator is fixed will cause a 3-way-merge conflict (both sides changing the same lines) surfaced in the regeneration PR body — that conflict is the signal to run fern replay forget and drop this patch.

Change

Reverts the 8 list-of-primitive (List[str]) multipart fields to raw values so httpx emits repeated form fields, matching generator 5.22.2 semantics:

File Field(s)
speech_to_text/raw_client.py keyterms
dubbing/project/raw_client.py keyterms
music/raw_client.py tags
music/finetunes/raw_client.py tags
studio/projects/raw_client.py genres, pronunciation_dictionary_locators, voice_settings
audio_native/raw_client.py pronunciation_dictionary_locators

Object fields (labels, entity_detection, entity_redaction, webhook_metadata) keep json.dumps — correct for multipart. Now-unused json / jsonable_encoder imports removed.

Durability

These are generated files, so the edit is carried across regenerations by Fern Replay (auto-captured as a patch in .fern/replay.lock). See the revert conditions above for how it is retired.

Test plan

  • ruff@0.11.5 (E/F/I) passes on all 6 files
  • py_compile passes on all 6 files
  • Verify speech_to_text.convert(keyterms=[...]) no longer returns invalid_keyword_length against the live API
  • Confirm Fern Replay captures the patch on the next regeneration (patches detected: 1)

Fixes #819

🤖 Generated with Claude Code

…819)

Interim hotfix for the v2.59.0 regression introduced by generator
fern-python-sdk 4.46.9 (commit cb4feb824, PR fern-api/fern#11284), which
began serializing every non-scalar multipart form field via
json.dumps(jsonable_encoder(...)). List[str] fields are not scalar
primitives, so they were sent as a single JSON-array string instead of
repeated form fields -- causing the API to validate the whole array
against per-item limits (e.g. speech_to_text keyterms ->
invalid_keyword_length).

Reverts the 8 list-of-primitive fields to raw values so httpx emits
repeated form fields, matching generator fix 5.22.2. Object fields
(labels, entity_detection, entity_redaction, webhook_metadata) keep
json.dumps. Removes now-unused json / jsonable_encoder imports.

This is intended to be carried across regenerations by Fern Replay and
retired (`fern replay forget`) once the 4.x backport of 5.22.2 lands.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kraenhansen
kraenhansen requested a review from PaulAsjes July 29, 2026 20:17
@kraenhansen kraenhansen self-assigned this Jul 29, 2026
@kraenhansen
kraenhansen merged commit 560b893 into main Jul 30, 2026
4 checks passed
@kraenhansen
kraenhansen deleted the kh/fix-819-multipart-list-serialization branch July 30, 2026 07:49
kraenhansen pushed a commit that referenced this pull request Jul 30, 2026
* [fern-generated] Update SDK

Generated by Fern
CLI Version: unknown
Generators:
  - fernapi/fern-python-sdk: 4.64.1

* [fern-replay] Applied customizations

Patches applied (1):
  - patch-560b8934: fix: send list-of-primitive multipart fields as repeated form fields (#819) (#825)

Patches with unresolved conflicts (1):
  - patch-1b45413e: [Speech Engine] Add disable_auth parameter (#806)
    Run `fern-replay resolve` to apply these customizations.

---------

Co-authored-by: fern-api[bot] <115122769+fern-api[bot]@users.noreply.github.com>
kraenhansen added a commit that referenced this pull request Jul 30, 2026
…#828)

patch-1b45413e was captured from "[Speech Engine] Add disable_auth
parameter (#806)", but its remaining diff is only a hardcoded version
bump (2.55.0 to 2.56.0) plus websockets >=13.0 (now native via the
generator config). Because it pins an old version string, it collided
with the version line on every regeneration and surfaced as an
unresolved conflict (most recently in the v2.60.0 regen PR #826).

Running `fern replay forget patch-1b45413e` drops it from the active
patch set and tombstones its content hash so it is not re-captured.
The #825 multipart fix (patch-560b8934) is unaffected, and disable_auth
itself is generated from the spec and does not depend on this patch.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
kraenhansen pushed a commit that referenced this pull request Aug 5, 2026
* [fern-generated] Update SDK

Generated by Fern
CLI Version: unknown
Generators:
  - fernapi/fern-python-sdk: 4.64.1

* [fern-replay] Applied customizations

Patches applied (1):
  - patch-560b8934: fix: send list-of-primitive multipart fields as repeated form fields (#819) (#825)

---------

Co-authored-by: fern-api[bot] <115122769+fern-api[bot]@users.noreply.github.com>
@fern-api fern-api Bot mentioned this pull request Aug 6, 2026
kraenhansen added a commit that referenced this pull request Aug 6, 2026
The create-dubbing-project endpoint gained a new webhook_ids field, which
shifted the context of the Fern Replay patch #825 so its body hunk no longer
applied: `import json` was removed but `json.dumps(...)` was left behind for
both keyterms and webhook_ids, breaking mypy (and raising NameError at runtime).

Extend the fix to both fields so it matches the TS SDK (repeated form fields).
Fern Replay will re-derive the patch to cover webhook_ids on the next regen.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
kraenhansen added a commit that referenced this pull request Aug 6, 2026
* [fern-generated] Update SDK

Generated by Fern
CLI Version: unknown
Generators:
  - fernapi/fern-python-sdk: 4.64.1

* [fern-replay] Applied customizations

Patches with unresolved conflicts (1):
  - patch-560b8934: fix: send list-of-primitive multipart fields as repeated form fields (#819) (#825)
    Run `fern-replay resolve` to apply these customizations.

* fix: send dubbing keyterms/webhook_ids as repeated multipart form fields

The create-dubbing-project endpoint gained a new webhook_ids field, which
shifted the context of the Fern Replay patch #825 so its body hunk no longer
applied: `import json` was removed but `json.dumps(...)` was left behind for
both keyterms and webhook_ids, breaking mypy (and raising NameError at runtime).

Extend the fix to both fields so it matches the TS SDK (repeated form fields).
Fern Replay will re-derive the patch to cover webhook_ids on the next regen.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: fern-api[bot] <115122769+fern-api[bot]@users.noreply.github.com>
Co-authored-by: Kræn Hansen <kraen@elevenlabs.io>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@fern-api fern-api Bot mentioned this pull request Aug 11, 2026
kraenhansen added a commit that referenced this pull request Aug 11, 2026
Fern regeneration reverted the #825 fix and dropped the json import,
breaking mypy and re-introducing the original multipart bug.
kraenhansen added a commit that referenced this pull request Aug 11, 2026
* [fern-generated] Update SDK

Generated by Fern
CLI Version: unknown
Generators:
  - fernapi/fern-python-sdk: 4.64.1

* [fern-replay] Applied customizations

Patches with unresolved conflicts (1):
  - patch-560b8934: fix: send list-of-primitive multipart fields as repeated form fields (#819) (#825)
    Run `fern-replay resolve` to apply these customizations.

Patches absorbed by generator (1):
  - patch-e1cf8db9: Add missing realtime speech-to-text options (#830)
    The generator now produces these customizations natively.

* fix: preserve repeated-form-field encoding for keyterms/webhook_ids

Fern regeneration reverted the #825 fix and dropped the json import,
breaking mypy and re-introducing the original multipart bug.

---------

Co-authored-by: fern-api[bot] <115122769+fern-api[bot]@users.noreply.github.com>
Co-authored-by: Kræn Hansen <kraen@elevenlabs.io>
PaulAsjes added a commit that referenced this pull request Aug 12, 2026
…ients

- delete src/elevenlabs/realtime/, realtime_tts.py and the
  speech_to_text_custom wrapper: client.text_to_speech.realtime(...) and
  client.speech_to_text.realtime(...) are now generated (sync + async)
- narrow the speech_engine .fernignore entry from the whole directory to
  the hand-written session/server/resource layer only, so the generated
  REST + socket clients regenerate in place; hand-written types.py renamed
  to session_types.py to make room for the generated types/ package
- restore music/raw_client.py to pure generated output and prune the
  stale replay.lock patch (#825) — the 5.x generator implements the
  repeated-form-fields fix natively
- CI bumps python 3.8 -> 3.10 (quoted; unquoted 3.10 parses as 3.1)
- test_ttv switched from removed create_previews to design

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@fern-api fern-api Bot mentioned this pull request Aug 13, 2026
kraenhansen added a commit that referenced this pull request Aug 13, 2026
Fern regeneration reverted the #825 fix and dropped the json import,
breaking mypy and re-introducing the original multipart bug.
kraenhansen added a commit that referenced this pull request Aug 13, 2026
* [fern-generated] Update SDK

Generated by Fern
CLI Version: unknown
Generators:
  - fernapi/fern-python-sdk: 4.64.1

* [fern-replay] Applied customizations

Patches with unresolved conflicts (1):
  - patch-560b8934: fix: send list-of-primitive multipart fields as repeated form fields (#819) (#825)
    Run `fern-replay resolve` to apply these customizations.

* fix: preserve repeated-form-field encoding for keyterms/webhook_ids

Fern regeneration reverted the #825 fix and dropped the json import,
breaking mypy and re-introducing the original multipart bug.

---------

Co-authored-by: fern-api[bot] <115122769+fern-api[bot]@users.noreply.github.com>
Co-authored-by: Kræn Hansen <kraen@elevenlabs.io>
@fern-api fern-api Bot mentioned this pull request Aug 24, 2026
kraenhansen added a commit that referenced this pull request Aug 25, 2026
Fern regeneration reverted the #825 fix again, re-wrapping both fields in
json.dumps and dropping the json import, which breaks mypy and re-introduces
the original multipart bug. Same recurrence as #845.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@fern-api fern-api Bot mentioned this pull request Aug 25, 2026
kraenhansen added a commit that referenced this pull request Aug 25, 2026
Fern regeneration reverted the #825 fix again, re-wrapping both fields in
json.dumps and dropping the json import, which breaks mypy and re-introduces
the original multipart bug. Third recurrence, after #845 and the fix on the
branch closed with #851.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
kraenhansen added a commit that referenced this pull request Aug 25, 2026
Fern regeneration reverted the #825 fix again, re-wrapping both fields in
json.dumps and dropping the json import, which breaks mypy and re-introduces
the original multipart bug. Third recurrence, after #845 and the fix on the
branch closed with #851.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
kraenhansen added a commit that referenced this pull request Aug 25, 2026
* [fern-generated] Update SDK

Generated by Fern
CLI Version: unknown
Generators:
  - fernapi/fern-python-sdk: 4.64.1

* [fern-replay] Applied customizations

Patches with unresolved conflicts (1):
  - patch-560b8934: fix: send list-of-primitive multipart fields as repeated form fields (#819) (#825)
    Run `fern-replay resolve` to apply these customizations.

* fix: preserve repeated-form-field encoding for keyterms/webhook_ids

Fern regeneration reverted the #825 fix again, re-wrapping both fields in
json.dumps and dropping the json import, which breaks mypy and re-introduces
the original multipart bug. Third recurrence, after #845 and the fix on the
branch closed with #851.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: fern-api[bot] <115122769+fern-api[bot]@users.noreply.github.com>
Co-authored-by: Kræn Hansen <kraen@elevenlabs.io>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

keyterms parameter causes invalid_keyword_length error in v2.59.0 due to json.dumps() serialization regression

2 participants