Skip to content
Developer Preview

Backpressure

Backpressure is what the protocol does when one side produces faster than the other side consumes. The server uses bounded queues at three points — transport ingress, request processing, and per-connection outbound writes — and the contract at each is explicit. Your client has one duty on each side: back off when the server says it is overloaded, and catch up by cursor when the server says it dropped push deliveries.

When the server’s ingress or processing queue is full, new requests are answered immediately with the typed overloaded error rather than being buffered. That error is retryable: retry with exponential backoff and jitter. Client notifications — initialized is the only one today — are never the cause of ingress saturation.

The command plane has its own admission queue on top of this, which reports a distinct typed backpressured error carrying the queue capacity in its data. Treat the two the same way operationally — back off, then retry — but they are different kinds and different codes; branch on data.kind.

Each connection has a bounded outbound notification queue. When your client reads too slowly and that queue fills, the server does not stall the session and does not silently discard events. Instead it collapses the queued backlog per subscription into a single gap notification — view/gap for session-view subscriptions.

A view/gap means exactly this: events after your last delivered cursor were not pushed to you, and you must catch up with a cursor read (view/page) before treating your local state as current. Three properties make gaps safe to build on:

  • Nothing is ever dropped without a gap marking the hole.
  • The gap is a push-delivery statement only — the underlying durable log is untouched, so a cursor read recovers everything.
  • The gap notification itself is protected (below), so it cannot be lost to the same congestion it reports.

After a view/gap, splice-fill the missing range or re-anchor before rendering the fold as current; that duty is a client MUST.

Some frames are never dropped, never gapped, and cannot be opted out; they ride a separate small reserved queue:

  • responses and error responses;
  • server-initiated requests and their settlement notifications — approval/request and userInput/request, the JSON-RPC request frames that present an approval or a user-input prompt, together with the approval/resolved and userInput/settled notifications that settle them. The approval/requested and userInput/requested view events are ordinary notifications — gap-able, recoverable by cursor — not members of this set;
  • view/gap and its raw-log analogue, and the deprecation notice.

The design consequence for your client: correctness-critical frames arrive or the connection is dead — you never need a timeout-based guess about whether an approval resolution was dropped in transit.

This pairs with the opt-out mechanism from the handshake: a client may suppress named notification methods for the connection (for example the high-volume streaming delta events), but the protected set can never be opted out.

The disconnect policy is per transport:

  • stdio — the transport you build against in v1 — the server never unilaterally closes the pipe; if even a protected write would block indefinitely, the server keeps the write blocked, because the single client owns its own liveness. A wedged client therefore wedges itself, not the session.
  • unix socket / websocket (deferred transports): a connection whose protected queue stays full past a write timeout is closed; ordinary slowness never causes disconnect — only inability to deliver protected frames does. The disconnected client reconnects, re-initializes, and resumes by cursor.
  1. Drain stdout continuously on a dedicated reader; never block your frame reader on your own UI or disk work, because the stdio server will not sever a wedged pipe for you.
  2. Handle overloaded with jittered exponential backoff on the request path.
  3. Treat view/gap as a state machine input, not a warning: mark the fold stale, page the missing range by cursor, and only then resume rendering as current.
  4. Never rely on receiving an opted-out or unprotected notification for correctness; anything you cannot afford to miss is either protected or recoverable by cursor read.