> For the complete documentation index, see [llms.txt](https://docs.gammaswap.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.gammaswap.com/developers/guides/websocket-reconnection.md).

# WebSocket Reconnection

The market and oracle WebSocket clients reconnect automatically by default.

```ts
const marketStream =
  createExchangeWebSocketClient({
    websocketUrl:
      "wss://exchange-api.gammaswap.com/ws/",
    reconnect: true,
  });

const oracleStream =
  createOracleWebSocketClient({
    websocketUrl:
      "wss://exchange-api.gammaswap.com/oracle-ws/",
    reconnect: true,
  });
```

Reconnection keeps the transport available, but each stream has different recovery requirements.

### Default reconnect configuration

<table><thead><tr><th width="263.7890625">Option</th><th align="right">Default</th></tr></thead><tbody><tr><td><code>reconnect</code></td><td align="right"><code>true</code></td></tr><tr><td><code>reconnectDelayMs</code></td><td align="right"><code>1000</code></td></tr><tr><td><code>maxReconnectDelayMs</code></td><td align="right"><code>30000</code></td></tr><tr><td><code>ackTimeoutMs</code></td><td align="right"><code>15000</code></td></tr><tr><td>Oracle <code>stalePriceTimeoutMs</code></td><td align="right"><code>30000</code></td></tr></tbody></table>

Configure the reconnect delay when creating a client:

```ts
const marketStream =
  createExchangeWebSocketClient({
    websocketUrl:
      "wss://exchange-api.gammaswap.com/ws/",
    reconnect: true,
    reconnectDelayMs: 1_000,
    maxReconnectDelayMs: 30_000,
    ackTimeoutMs: 15_000,
    onError: (error) => {
      console.error(
        "Market WebSocket error",
        error,
      );
    },
  });
```

### Market WebSocket recovery

Market updates include sequence IDs.

After the connection drops or the SDK determines that the socket is unhealthy, active handlers receive:

```ts
onResyncRequired(assetId);
```

Example:

```ts
const unsubscribe =
  await marketStream.subscribeOrderBook(
    assetId,
    {
      onUpdate: (update) => {
        applyMarketUpdate(update);
      },
      onResyncRequired: async (
        resyncAssetId,
      ) => {
        const freshBook =
          await info.getOrderBook({
            assetId: resyncAssetId,
            epoch,
          });

        replaceLocalBook(freshBook);
      },
      onError: (error) => {
        console.error(
          "Market subscription error",
          error,
        );
      },
    },
  );
```

Do not continue applying market updates to the old local order book after `onResyncRequired` is called.

Instead:

1. Mark the local order book as unsynchronized.
2. Buffer or pause new events.
3. Load a fresh HTTP order-book snapshot.
4. Replace the local state.
5. Apply only events newer than the snapshot sequence ID.
6. Resume normal processing.

### Market reconnect flow

```
Connection becomes unhealthy
        ↓
onResyncRequired(assetId)
        ↓
SDK reconnects when subscriptions remain
        ↓
Reload GET /book/:assetId/:epoch
        ↓
Replace the local order book
        ↓
Resume live updates
```

### Oracle WebSocket recovery

Oracle price updates do not include sequence IDs.

The oracle stream also does not provide a REST catch-up operation.

After reconnecting, accept the next live price update.

```ts
const unsubscribe =
  await oracleStream.subscribePrice(
    "1",
    {
      onPrice: (update) => {
        setCurrentPrice(
          update.symbolId,
          update.price,
          update.ts,
        );
      },
      onStale: (symbolId) => {
        markPriceAsStale(symbolId);
      },
      onError: (error) => {
        console.error(
          "Oracle subscription error",
          error,
        );
      },
    },
  );
```

### Stale oracle prices

`stalePriceTimeoutMs` controls how long the client waits without a price update for a subscribed symbol.

```ts
const oracleStream =
  createOracleWebSocketClient({
    websocketUrl:
      "wss://exchange-api.gammaswap.com/oracle-ws/",
    stalePriceTimeoutMs: 30_000,
  });
```

If the timeout expires, the client:

1. Calls `onStale(symbolId)`.
2. Emits an error.
3. Abandons the unhealthy socket.
4. Reconnects when active subscriptions remain.

Example stale callback:

```ts
onStale: (symbolId) => {
  console.warn(
    "Oracle price is stale",
    symbolId,
  );

  disablePriceDependentActions(symbolId);
}
```

When the next price arrives:

```ts
onPrice: (update) => {
  clearStaleState(update.symbolId);
  setCurrentPrice(
    update.symbolId,
    update.price,
    update.ts,
  );
}
```

### Subscription acknowledgement timeout

The clients wait for subscribe and unsubscribe acknowledgements.

The default timeout is:

```ts
15_000
```

Configure it with `ackTimeoutMs`:

```ts
const marketStream =
  createExchangeWebSocketClient({
    websocketUrl:
      "wss://exchange-api.gammaswap.com/ws/",
    ackTimeoutMs: 20_000,
  });
```

A failed subscription acknowledgement rejects the subscription operation or emits an error through the configured handlers.

### Unsubscribe failures

Unsubscribe acknowledgements are best-effort.

If an unsubscribe acknowledgement fails:

* The local subscription is still removed.
* The error is emitted.
* The client reconnects only when other active subscriptions remain.

The application should treat the local handler as removed even if the server acknowledgement is not received.

### Multiple subscriptions

One WebSocket client can manage multiple asset or symbol subscriptions.

Market example:

```ts
const unsubscribeAssetOne =
  await marketStream.subscribeOrderBook(
    assetOne,
    assetOneHandlers,
  );

const unsubscribeAssetTwo =
  await marketStream.subscribeOrderBook(
    assetTwo,
    assetTwoHandlers,
  );
```

Oracle example:

```ts
const unsubscribeSymbolOne =
  await oracleStream.subscribePrice(
    "1",
    symbolOneHandlers,
  );

const unsubscribeSymbolTwo =
  await oracleStream.subscribePrice(
    "2",
    symbolTwoHandlers,
  );
```

The client maintains active local handlers across transport failures. Recovery logic must still restore application state:

* Reload market order books after market reconnects.
* Accept the next live price after oracle reconnects.

### Shared server subscriptions

Multiple local handlers for the same asset or symbol share one server subscription.

```ts
const unsubscribeA =
  await marketStream.subscribeOrderBook(
    assetId,
    handlersA,
  );

const unsubscribeB =
  await marketStream.subscribeOrderBook(
    assetId,
    handlersB,
  );
```

Calling:

```ts
await unsubscribeA();
```

removes only `handlersA`.

The server subscription remains active while `handlersB` is still registered.

Calling:

```ts
await marketStream.unsubscribeOrderBook(
  assetId,
);
```

removes all local handlers for the asset.

### Intentional close

Calling `close()` intentionally closes the connection.

```ts
marketStream.close(
  1000,
  "Application shutdown",
);

oracleStream.close(
  1000,
  "Application shutdown",
);
```

An intentional close should be part of the application’s shutdown process.

Run returned unsubscribe functions before closing when practical:

```ts
await unsubscribe();
marketStream.close();
```

### Browser and Node.js behavior

Browser WebSocket implementations support `close()` but do not provide a forceful termination method.

The Node `ws` implementation can provide `terminate()`.

When a socket is already considered unhealthy, the SDK:

* Uses `terminate()` when the WebSocket implementation provides it
* Falls back to `close()` in browser-compatible environments
* Ignores events from abandoned sockets so stale events do not affect a newer connection

If an abandoned browser socket does not close cleanly, the server heartbeat or TCP timeout eventually removes it.

### Heartbeats

The services send protocol-level WebSocket ping frames.

Standard browser WebSockets and the Node `ws` package automatically respond with protocol pong frames.

Do not send an application-level pong:

```json
{
  "type": "pong"
}
```

### Error handling

Configure a client-level error handler:

```ts
const marketStream =
  createExchangeWebSocketClient({
    websocketUrl:
      "wss://exchange-api.gammaswap.com/ws/",
    onError: (error) => {
      console.error(
        "Market WebSocket client error",
        error,
      );
    },
  });
```

Also configure subscription-level error handlers:

```ts
await marketStream.subscribeOrderBook(
  assetId,
  {
    onUpdate: applyMarketUpdate,
    onError: (error) => {
      console.error(
        "Asset subscription error",
        error,
      );
    },
    onResyncRequired: () => {
      void reloadOrderBook();
    },
  },
);
```

### Disable automatic reconnection

Automatic reconnection can be disabled:

```ts
const marketStream =
  createExchangeWebSocketClient({
    websocketUrl:
      "wss://exchange-api.gammaswap.com/ws/",
    reconnect: false,
  });
```

When disabled, the application is responsible for:

* Detecting connection failure
* Opening a new connection
* Recreating subscriptions
* Reloading market state
* Marking oracle prices as stale

### Recommended reconnect behavior

For market subscriptions:

1. Enable automatic reconnection.
2. Implement `onResyncRequired`.
3. Pause or buffer market events during resynchronization.
4. Reload the full HTTP order-book snapshot.
5. Validate sequence continuity before resuming.

For oracle subscriptions:

1. Enable automatic reconnection.
2. Configure `stalePriceTimeoutMs`.
3. Implement `onStale`.
4. Mark stale prices as unusable.
5. Accept the next live price after reconnecting.

For both clients:

* Log errors without exposing secrets.
* Monitor repeated reconnect attempts.
* Avoid creating a new client for every subscription.
* Close clients during application shutdown.
* Keep client and subscription error handlers lightweight.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.gammaswap.com/developers/guides/websocket-reconnection.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
