WebSocket Close Codes
When the server closes a WebSocket it carries a numeric close code. Clients should branch on this code, not on the close-reason text.
Standard close codes (RFC 6455)
| Code | Name | When you see it | What to do |
|---|---|---|---|
| 1001 | Going Away | Server is shutting down (deploy, rolling restart). Comes after a going_away JSON message with retryAfterMs. | Wait the suggested interval, reconnect. Not an error. |
| 1008 | Policy Violation | Generic protocol violation. Rare; usually paired with a more specific 4xxx code. | Inspect the close reason; fix the violation. |
| 1009 | Message Too Big | You sent an inbound frame > 64 KB. | Split the payload; the cap is per-frame, not per-session. |
Application close codes (4xxx)
These are Metered-specific and documented as part of the wire protocol. RFC 6455 reserves the 4000–4999 range for application use.
| Code | Name | When you see it | What to do |
|---|---|---|---|
| 4001 | Invalid Token | JWT failed verification (bad signature, malformed, key not found). | Check your sk_ signing secret + kid header. Re-mint and reconnect. |
| 4002 | Token Expired | JWT's exp has passed. The server force-closes ~250ms before exp. | Mint a fresh token from your backend; reconnect. |
| 4003 | Channel Not Authorized | You tried to subscribe to a channel outside your key's channelPatterns. | Fix the key's patterns on the dashboard, or pick a different channel. |
| 4010 | Over Concurrent Limit | Your plan's maxConcurrentConnections hit AND overages are disabled (or balance exhausted with auto-recharge off). | Wait and retry, or enable overages / upgrade the plan. |
| 4011 | Over Message Rate | Per-connection token bucket emptied (sustained > 100 msg/sec or burst > 200). This is an abuse bound, not a billing limit. | Throttle on the client. If you genuinely need >100 msg/sec sustained per peer, contact sales. |
| 4012 | Account Suspended | The customer's account is suspended (billing failure, ToS issue). | Resolve in the dashboard. Reconnect attempts will keep failing until the account is reactivated. |
| 4020 | Admin Disconnect | A backend invoked DELETE /v1/peers/:id against this peer. | Retry with a fresh token — typically a customer-issued logout / kick. |
Pre-handshake HTTP rejections
Some failures happen before the WebSocket handshake completes, so the client sees an HTTP status instead of a close code:
| HTTP | Reason |
|---|---|
| 401 | Auth failed (invalid / expired / missing token, key not found, origin not allowed) |
| 404 | Wrong path — the only valid upgrade endpoint is /v1 |
| 429 | Per-IP connect rate limit exceeded (anti-abuse) |
| 500 / 503 | Internal failure during upgrade |
Reconnect strategy
| Close code | Recommended reconnect behavior |
|---|---|
1001 Going Away | Wait retryAfterMs from the going_away JSON message. |
1008 / 1009 | Don't reconnect — fix the underlying issue first. |
4001 / 4002 | Mint a fresh token, reconnect. |
4003 | Don't reconnect — fix the channel auth on the backend. |
4010 | Exponential backoff. Could be transient (other peers disconnecting frees a slot). |
4011 | Exponential backoff. Don't immediately republish; you'll just trip the abuse bound again. |
4012 | Stop. Show a user-facing message. The account is suspended. |
4020 | Mint a fresh token; reconnect once. If the kick was the customer's intent (e.g., logout), don't auto-reconnect. |
A reasonable default backoff: starting at 1s, exponential to 30s, plus ±20% jitter.