> ## Documentation Index
> Fetch the complete documentation index at: https://www.helius.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Handling Reconnects and Errors in Parsed Streams

> Detect Parsed Streams disconnects, reconnect with backoff, resubscribe, and backfill exactly the slot window you missed.

Delivery is **at most once** — there is no replay. Whatever confirmed while you were disconnected is not resent, so a production client needs to detect the gap and decide whether to backfill it. The signal for that is `context.slot`: it bounds the window you missed across a disconnect. This guide covers why connections close, how to reconnect cleanly, and how to use it.

## Why Connections Close

Every close has a WebSocket close code that tells you what happened and what to do next:

| Code        | Reason                                                       | What to do                                                                                                                                                                          |
| ----------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1000        | Idle: no client messages and no notifications for 10 minutes | Send the JSON-RPC `ping` method every few minutes if your filter is quiet. Protocol-level WebSocket pings that client libraries send automatically do **not** reset the idle timer. |
| 1001        | Server restart (deploy)                                      | Reconnect and resubscribe                                                                                                                                                           |
| 1008        | Slow consumer: you fell more than 2048 notifications behind  | Reconnect with a narrower filter, `details: "matched"` or `"raw"`, or faster processing                                                                                             |
| 1005 / 1006 | Network edge recycled the connection                         | Reconnect and resubscribe; normal at some frequency on any long-lived connection                                                                                                    |

The server pings every 15 seconds, so a healthy but quiet connection still carries traffic. If you see nothing at all for over a minute — no notification, no ping — assume the connection is dead and reconnect rather than waiting for the socket to tell you.

## Keep the Connection Alive

If your filter is narrow enough that it can legitimately go 10 minutes without a match, send an explicit JSON-RPC `ping` on an interval shorter than that:

```json theme={"system"}
{ "jsonrpc": "2.0", "id": 99, "method": "ping" }
```

It returns the current slot and, more importantly, counts as a client message for the idle timer. A library-level WebSocket ping frame does not.

## Reconnect and Detect the Gap

<Steps>
  <Step title="Reconnect with Backoff">
    On any close — expected or not — reconnect with exponential backoff. Subscription ids do not survive a reconnect, so resend `parsedTransactionSubscribe` for every filter you had open.
  </Step>

  <Step title="Track context.slot Across Disconnects">
    Keep the last `context.slot` you saw before the disconnect. The gap between that slot and the first slot you see after reconnecting is exactly the window you missed — nothing more, nothing less.
  </Step>

  <Step title="Backfill if You Need To">
    If your application can't tolerate the gap, backfill that slot window from RPC: `getSignaturesForAddress` to enumerate transactions in range, then `getTransaction` to fetch each one. This is a manual reconciliation step — Parsed Streams itself does not replay.
  </Step>
</Steps>

```typescript theme={"system"}
import WebSocket from "ws";

const URL = "wss://<ENDPOINT>/?api-key=<API_KEY>";
const filters = [
  { programs: ["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"] },
];

let lastSlotSeen: number | null = null;
let backoffMs = 1000;

function connect() {
  const ws = new WebSocket(URL);

  ws.on("open", () => {
    backoffMs = 1000;
    filters.forEach((filter, i) => {
      ws.send(JSON.stringify({
        jsonrpc: "2.0",
        id: i + 1,
        method: "parsedTransactionSubscribe",
        params: [filter],
      }));
    });
  });

  ws.on("message", (data) => {
    const msg = JSON.parse(data.toString());
    if (msg.method === "parsedTransactionNotification") {
      const { slot } = msg.params.result.context;

      // Across reconnects, the slot bounds the backfill window.
      if (lastSlotSeen !== null && slot > lastSlotSeen) {
        // backfill candidates: slots lastSlotSeen+1 .. slot-1 while disconnected
      }
      lastSlotSeen = slot;
    }
  });

  ws.on("close", (code) => {
    console.warn(`connection closed (${code}); reconnecting in ${backoffMs}ms`);
    setTimeout(connect, backoffMs);
    backoffMs = Math.min(backoffMs * 2, 30_000);
  });
}

connect();
```

`context.slot` is what survives a reconnect: track the highest slot you fully processed before the disconnect and treat everything after it as the backfill window.

## Handling JSON-RPC Errors

Requests that fail return a JSON-RPC error instead of a result, so you can branch on `error.code`:

| Code     | Meaning                                                                            |
| -------- | ---------------------------------------------------------------------------------- |
| `-32700` | Parse error (invalid JSON)                                                         |
| `-32600` | Invalid request                                                                    |
| `-32601` | Method not found                                                                   |
| `-32602` | Invalid params: bad pubkey, unknown field, unsupported commitment or details value |
| `-32000` | Filter limit exceeded                                                              |
| `-32001` | Server not ready; retry with backoff                                               |
| `-32002` | Rate limited (10 messages per second)                                              |
| `-32006` | Too many subscriptions (25 per connection)                                         |

`-32602` and `-32000` mean the request itself is wrong — fix the filter, don't retry it as is. `-32001` and `-32002` are transient; retry with the same backoff you use for reconnects.

## Next Steps

<CardGroup cols={2}>
  <Card title="Quickstart" icon="bolt" href="/docs/parsed-streams/quickstart">
    Full protocol reference: methods, filter fields, limits.
  </Card>

  <Card title="Track Jupiter Swaps" icon="arrow-right-arrow-left" href="/docs/parsed-streams/guides/track-jupiter-swaps">
    Build a filter this connection can subscribe to.
  </Card>
</CardGroup>
