fix(music): reject unknown body fields instead of dropping them

Merged
SW
2mo ago
+48-2

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_id rather 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.