RPC error WS close 4029: WebSocket stream limit reached
Last updated:
At a glance
| Property | Value |
|---|---|
| Applies to | Every network, HTTP and WebSocket |
| Type | WebSocket close code |
| Frame fields | error, message, current_tier, category, limit_streams or limit_rps, retry_after_sec |
| Related codes | 4001 auth, 4003 plan or method, 4030 trial ended |
| Shred stream | Replaces the oldest connection: superseded_by_new_connection |
| Retryable | After closing a stream, or after retry_after_sec |
Cause
A concurrent-stream cap is a count, not a rate, so the frame carries limit_streams rather than limit_rps. The usual cause is a reconnect that opens the new socket before the old one has gone: the old connection still holds its slot, so the new one is refused and the loop feeds itself. A second cause is a burst of subscribe calls right after connecting. On the Shred stream, the behaviour differs: a new connection replaces the key's oldest one, and the old socket closes with reason superseded_by_new_connection.
Solution
- Log the close code and the reason together. Without the reason, 4029 and an ordinary disconnect look the same in most client libraries.
- Close old sockets before opening new ones, and use one connection per key where you can.
- Reconnect with exponential backoff and jitter, honouring
retry_after_secfrom the frame. - Stagger subscribe calls after connecting instead of sending them all at once.
- If you really need more streams, compare the limits per plan on the streaming docs.
Reconnect without making it worse
The safe order is: stop writing, close, wait with jitter, then connect. Keep subscription state in your client so a reconnect restores it at a measured pace, and fill the gap with a read afterwards, since a stream does not replay what you missed.
ws.onclose = (e) => {
console.warn("closed", e.code, e.reason);
const base = e.code === 4029 ? 5000 : 1000;
const wait = Math.min(base * 2 ** attempt, 60000) + Math.random() * 1000;
setTimeout(connect, wait);
};Example
{
"error": "rate_limited",
"message": "Your pro plan allows 8 concurrent streams on ... and all are in use. Close a stream before opening another, or upgrade",
"current_tier": "pro",
"limit_streams": 8,
"retry_after_sec": 5
}FAQ
- Why does 4029 keep coming back after a reconnect?
- The old connection still holds its slot until the server sees it gone. Close it explicitly, wait, and reconnect with backoff.
- What is the difference between 4029 and 4003?
- 4029 is a limit on how much you may use. 4003 means your plan or the method does not allow this at all, so retrying will not help.