Effect's default Respondable for HttpApiSchemaError returns 400 with an
empty body. The renderer / SDK / curl get nothing actionable — just
"GET /url → 400 Bad Request: (empty response body)". When a real user
hit this on Windows yesterday (corrupted DB row → schema rejected the
response), we spent ~an hour reverse-engineering the cause from the
URL alone.
PR #26457 previously tried to surface the reason in a structured body
({data, errors, success}) and got reverted in #26546 because some
plugins broke. The proximate cause was the SDK throwing raw POJOs to
plugins instead of Errors, which has since been fixed by
`wrapClientError` (`50dcc4f1a`).
Use the same NamedError shape every other 4xx/5xx in this API already
uses (e.g. NotFoundError 404):
{"name":"BadRequest","data":{"message":"...","kind":"Body"}}
The SDK's wrapClientError extracts data.message automatically, so any
caller that handles existing 404 NotFoundError bodies handles this
identically — no new contract.
Verified end-to-end:
BEFORE
status: 400
body: ""
SDK Error.message: opencode server GET .../message?... → 400: (empty response body)
AFTER
status: 400
body: {"name":"BadRequest","data":{"message":"Expected number, got null
at [0][\"parts\"][0][\"tokens\"][\"output\"]","kind":"Body"}}
SDK Error.message: Expected number, got null
at [0]["parts"][0]["tokens"]["output"]
Includes a regression test that asserts the body shape on a real Body
schema rejection (POST /sync/history with invalid aggregate).