## TL;DR
When the Cartesia TTS websocket upgrade is refused, the server answers with an ordinary HTTP error before the socket opens. Your client must read that HTTP status and body, because it names the real cause: unknown voice, invalid key, exhausted balance, or a full rate limit. Fix the named cause, then reconnect.

```text
cartesia websocket unexpected-response HTTP error before upgrade
```

## Use this when
- The websocket to the Cartesia TTS endpoint never opens.
- Your client logs an unexpected-response event with an HTTP status code.
- The same code worked before and now fails at connect time.
- You need to tell apart auth, voice, billing, and rate-limit failures.

## Not for this skill when
- The socket connects and drops mid-stream. That is a different failure.
- The session ends at 5 minutes or after 60 idle seconds. Those are documented limits.
- Audio connects but nothing plays. That is a playback or format problem.

## Steps
1. Add an unexpected-response handler that prints the HTTP status code and the full response body, then destroys the request. Verify: you see the status and body in your logs on the next failure.
2. Read the body. A 401 names the key, a 400 names the voice or a rejected setting, a 402 or 429 names billing or rate limits. Verify: you can state the cause in one sentence.
3. Fix the named cause: rotate or re-enter the API key for 401s, use a voice UUID from your own account for voice errors, top up credits or wait out the window for 402/429s. Verify: you know which of the four it was before retrying.
4. Reconnect and confirm the upgrade succeeds and audio chunks arrive. Verify: the handler stays quiet and the first audio chunk lands.

Expected output of each step is named above. The final check is a websocket that upgrades cleanly and streams audio.

## Root cause, briefly
An unknown voice, an exhausted balance, or a full rate or concurrency limit is refused before the upgrade, as an ordinary HTTP error with the standard error envelope. The socket failure is just the messenger.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_ctg1kV_QpmHVgsDnrKZvHw
