Introducing RabbitStream⚡, earliest transaction detection from Solana Shreds with gRPC style filtering. Explore Now
shyft logo
Get API Key

Blogs

Dev Guides

How to reconnect and replay slots with Solana Yellowstone gRPC

Shyft Logo

Team Shyft

· January 24, 2026

In this article you will learn how to implement a reconnect logic for your Solana gRPC streams with replay functionality to make sure you don’t miss any slots.

Reconnect mechanism cover

Yellowstone gRPC is a powerful, production-ready and battle-tested tool for streaming real-time Solana data. But in real-world conditions, network hiccups or server restarts can cause dropped connections. Without a proper reconnect strategy, your application risks missing critical updates from the blockchain. To prevent this, it’s important to build a system that not only reconnects automatically, but also resumes data streaming from a specific slot — ensuring consistency, reliability, and zero missed events.

Before Getting Started

To get started, we will need a few things.

**Authentication: gRPC endpoint and gRPC token
**Shyft’s gRPC nodes are available in various locations all across EU and the US region. To access, we would require a region-specific gRPC endpoint and an access token, which is available to purchase on your Shyft Dashboard.

Get Team Shyft’s stories in your inbox

Join Medium for free to get updates from this writer.

**A server-side backend (like NodeJS) to receive gRPC data
**As gRPC services are unsupported in web-browsers, you would need a backend application such as C#, Go, Java, Python etc. to receive gRPC data.

Code Example: Implementing a Reconnection mechanism

To ensure the stream automatically recovers from temporary disconnections, we implement a simple reconnection loop using gRPC streaming. If the connection drops due to an error, the application waits for a short delay and then restarts the gRPC stream using the same subscription request. This ensures continuous data flow without manual intervention, even in unstable network conditions.

async function subscribeCommand(client: Client, args: SubscribeRequest) {  while (true) {    try {      await handleStream(client, args);     } catch (error) {      console.error("Stream error, retrying in 1 second...", error);      await new Promise((resolve) => setTimeout(resolve, 1000));          }  }}

The code illustrates a while loop in which the handleStream() function is called. The handleStream() function is responsible for subscribing and receiving the gRPC stream. As soon as the stream is interrupted, an error is thrown from the handle stream function, which is handled inside the loop. The loop then waits for a given timeout and iterates, resending the subscribe request.

You can checkout our docs, or simply run the Repl code here for the full gRPC code of the example above.

Code Example: Replaying Updates from a specific Slot

To avoid missing any Solana transactions during a disconnection, the stream tracks the last received slot from each transaction update. When the gRPC stream encounters an error, it attempts to reconnect and resumes from that exact slot using the fromSlot field in the SubscribeRequest. This logic ensures that no transaction updates are skipped across reconnections. A retry counter is used to prevent infinite attempts — after a limit is reached, the system falls back to streaming from the latest available slot instead.

require("dotenv").config();import Client, { CommitmentLevel } from "@triton-one/yellowstone-grpc";import { SubscribeRequest } from "@triton-one/yellowstone-grpc/dist/types/grpc/geyser";import * as bs58 from "bs58";const MAX_RETRY_WITH_LAST_SLOT = 30;const RETRY_DELAY_MS = 1000;const ADDRESS_TO_STREAM_FROM = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P";type StreamResult = {  lastSlot?: string;  hasRcvdMSg: boolean;};async function handleStream(  client: Client,  args: SubscribeRequest,  lastSlot?: string): Promise<StreamResult> {  const stream = await client.subscribe();  let hasRcvdMSg = false;  return new Promise((resolve, reject) => {    stream.on("data", (data) => {      const tx = data.transaction?.transaction?.transaction;      if (tx?.signatures?.[0]) {        const sig = bs58.encode(tx.signatures[0]);        console.log("Got tx:", sig);        lastSlot = data.transaction.slot;        hasRcvdMSg = true;      }    });    stream.on("error", (err) => {      stream.end();      reject({ error: err, lastSlot, hasRcvdMSg });    });    const finalize = () => resolve({ lastSlot, hasRcvdMSg });    stream.on("end", finalize);    stream.on("close", finalize);    stream.write(args, (err: any) => {      if (err) reject({ error: err, lastSlot, hasRcvdMSg });    });  });}async function subscribeCommand(client: Client, args: SubscribeRequest) {  let lastSlot: string | undefined;  let retryCount = 0;  while (true) {    try {      if (args.fromSlot) {        console.log("Starting stream from slot", args.fromSlot);      }      const result = await handleStream(client, args, lastSlot);      lastSlot = result.lastSlot;      if (result.hasRcvdMSg) retryCount = 0;    } catch (err: any) {      console.error(        `Stream error, retrying in ${RETRY_DELAY_MS / 1000} second...`      );      await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS));      lastSlot = err.lastSlot;      if (err.hasRcvdMSg) retryCount = 0;      if (lastSlot && retryCount < MAX_RETRY_WITH_LAST_SLOT) {        console.log(          `#${retryCount} retrying with last slot ${lastSlot}, remaining retries ${            MAX_RETRY_WITH_LAST_SLOT - retryCount          }`        );        args.fromSlot = lastSlot;        retryCount++;      } else {        console.log("Retrying from latest slot (no last slot available)");        delete args.fromSlot;        retryCount = 0;        lastSlot = undefined;      }    }  }}const client = new Client(process.env.GRPC_URL!, process.env.X_TOKEN!, {  "grpc.keepalive_permit_without_calls": 1,  "grpc.keepalive_time_ms": 10000,  "grpc.keepalive_timeout_ms": 1000,  "grpc.default_compression_algorithm": 2,});const req: SubscribeRequest = {  accounts: {},  slots: {},  transactions: {    pumpFun: {      vote: false,      failed: false,      accountInclude: [ADDRESS_TO_STREAM_FROM],      accountExclude: [],      accountRequired: [],    },  },  transactionsStatus: {},  blocks: {},  blocksMeta: {},  entry: {},  accountsDataSlice: [],  commitment: CommitmentLevel.CONFIRMED,};subscribeCommand(client, req);

Similar to the last approach, the infinite loop ensures the gRPC stream keeps reconnecting whenever it drops. We initialize two variables, one for storing the **lastSlot** i.e. the most recent slot received from the stream, and **retryCount** which limits the number of retries from a previous slot to avoid getting stuck on bad data or gaps.

if (args.fromSlot) {    console.log("Starting stream from slot", args.fromSlot);}

Before starting the gRPC stream, the code checks if a fromSlot is set. If it is, the stream will resume from that specific slot instead of starting from the latest block. The handleStream function opens the gRPC stream, listens for incoming Solana transaction data, and keeps track of the most recent slot received. If any data is received, it marks the stream as successful (hasRcvdMSg = true) and resets the retry counter so the system can keep retrying from the last known slot if needed.

const result = await handleStream(client, args, lastSlot);lastSlot = result.lastSlot;if (result.hasRcvdMSg) retryCount = 0;
  • If a lastSlot was successfully recorded before the error, it will be reused in the next gRPC reconnection attempt.
  • If the previous gRPC stream did deliver data, we reset retryCount.

Smart Fallback

if (lastSlot && retryCount < MAX_RETRY_WITH_LAST_SLOT) {  args.fromSlot = lastSlot;  retryCount++;} else {  delete args.fromSlot;  retryCount = 0;  lastSlot = undefined;}

This is the core resilience logic:

  • If we still have valid lastSlot and haven’t exceeded the retry limit, we attempt to resume from it again via the gRPC endpoint.
  • If we’ve retried too many times or don’t have a valid slot, we clear the fromSlot and let the gRPC stream start from the tip of the blockchain.

The complete code for this article is available in on GitHub — feel free to clone and test it out. We’ve also shared a collection of example use cases covering gRPC and DeFi on GitHub, which you can clone and experiment with.

Conclusion

Building a reconnect strategy with slot-based replay ensures your Solana application remains reliable and real-time — even through network hiccups. By tracking the last received slot and retrying intelligently, you can resume streaming from exactly where you left off, avoiding missed updates or duplicated data. This approach adds resilience and guarantees a smoother user experience for any production-grade blockchain app.

You can explore our other related articles: Streaming Real-Time Data on Solana, Real-Time Data Streaming with gRPC: Accounts, Transactions, Blocks, How to Stream Real-Time Pump.fun Updates on Solana, and Tracking New Pools on Raydium.

Resources

Related Posts

How to modify Solana Yellowstone gRPC subscribe requests without disconnecting
Shyft

How to modify Solana Yellowstone gRPC subscribe requests without disconnecting

Learn how to modify your yellowstone gRPC Subscribe Requests on Solana without stopping your stream or losing data ...

January 24, 2026

Real-Time Solana Data Streaming with gRPC: Accounts, Transactions, Blocks
Shyft

Real-Time Solana Data Streaming with gRPC: Accounts, Transactions, Blocks

A comprehensive guide on how to stream Transactions, Accounts, and Block updates swiftly using Shyft’s gRPC Services ...

January 22, 2026

Launching liquidity pools on Raydium with safeguarding strategy to counter bot manipulation (Part-2)
Shyft

Launching liquidity pools on Raydium with safeguarding strategy to counter bot manipulation (Part-2)

A step-by-step guide to launch token, maximize token’s visibility and trading volume using Jito bundles on Raydium In th...

January 22, 2026

Get in touch with our discord community and keep up with the latest feature
releases. Get help from our developers who are always here to help you take off.

GithubTwitterLinked inDiscordTelegramBlogsBlogs

Products

RabbitStreamgRPC NetworkSuperIndexerSolana APIs
Contact Us|Email: genesis@shyft.to