fix(music): reject unknown body fields instead of dropping them
Row 2 of the chat#1999 matrix.
The defect
POST /api/music with organization_id returns 200, files the song under the caller's personal account, and says nothing. Zod strips keys that are not in the schema, so a field that changes nothing looks identical to one that worked.
This already cost us once. chat#1994 shipped sending organization_id, and because nothing failed it had to be found by reading the code. Anyone working from the pre-docs#309 contract, or reasonably guessing the field exists, hits the same thing: an org-scoped generation silently lands as personal.
Now that the parameter is definitively not coming — org scope is expressed as account_id, because organizations are accounts — the silent drop gets worse, not better. It is the only feedback a caller gets, and it is wrong.
Change
z.object → z.strictObject on createMusicBodySchema.
The error message needed care: Zod reports an unrecognized key with an empty path and the offending names under keys, so the existing firstError.message / firstError.path handling would have returned a 400 that did not say which field was wrong. The message and missing_fields are now built from keys when the failure is unrecognized_keys, and left alone otherwise.
POST { prompt, lyrics, organization_id }
before: 200, song filed personally
after: 400 "Unrecognized field(s): organization_id"
Tests
TDD, both confirmed RED first. 61/61 green across lib/music; tsc and eslint clean.
- rejects
organization_idrather than dropping it, and names it in the message - names an arbitrary unknown field (
tempo) too, so this is not special-cased
Existing coverage still passes unchanged, including the test asserting that org scope works via account_id.
Blast radius
Deliberately music-only. Any client sending a stray field to this endpoint starts getting 400s — correct, but a behaviour change. The same fix is worth considering for other endpoints; doing it globally in one PR would make the blast radius much harder to reason about, so it is not done here.
🤖 Generated with Claude Code
https://claude.ai/code/session_017fSvwazBitPfsTQvVqpi8q
Summary by cubic
Reject unknown fields in POST /api/music and return a 400 that names them. Previously we dropped unknown fields, so requests with organization_id returned 200 and filed songs under the personal account.
Use z.strictObject in createMusicBodySchema and build error messages from Zod’s unrecognized_keys; other validations are unchanged.
Migration
- Stop sending organization_id to /api/music; use account_id for org scope.
- Remove unsupported fields; unknown keys now return 400 with “Unrecognized field(s) …”.
Summary by CodeRabbit
- Bug Fixes
- Requests with unrecognized fields are now rejected during music creation validation.
- Validation errors identify the specific unknown fields in both structured and human-readable messages.
- Existing validation behavior for other errors remains unchanged.