RPC error WS close 4029: WebSocket stream limit reached

Last updated:

At a glance

Error details
PropertyValue
Applies toEvery network, HTTP and WebSocket
TypeWebSocket close code
Frame fieldserror, message, current_tier, category, limit_streams or limit_rps, retry_after_sec
Related codes4001 auth, 4003 plan or method, 4030 trial ended
Shred streamReplaces the oldest connection: superseded_by_new_connection
RetryableAfter 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

  1. Log the close code and the reason together. Without the reason, 4029 and an ordinary disconnect look the same in most client libraries.
  2. Close old sockets before opening new ones, and use one connection per key where you can.
  3. Reconnect with exponential backoff and jitter, honouring retry_after_sec from the frame.
  4. Stagger subscribe calls after connecting instead of sending them all at once.
  5. 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.