LangChain Tutorial (TypeScript): implementing retry logic for intermediate developers

By Cyprian AaronsUpdated 2026-04-21
langchainimplementing-retry-logic-for-intermediate-developerstypescript

This tutorial shows you how to add retry logic around LangChain calls in TypeScript so transient failures don’t break your workflow. You need this when your model provider rate-limits, times out, or returns occasional 5xx errors and you want your agent or chain to recover automatically.

What You'll Need

  • Node.js 18+
  • TypeScript 5+
  • langchain
  • @langchain/openai
  • An OpenAI API key in OPENAI_API_KEY
  • A project initialized with ESM support or a modern TypeScript setup
  • Basic familiarity with LangChain Runnable patterns

Install the packages:

npm install langchain @langchain/openai
npm install -D typescript tsx @types/node

Step-by-Step

  1. Start with a plain LangChain runnable so you have something to wrap. The key idea is that retry logic belongs around the runnable invocation, not inside your business logic.
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage } from "@langchain/core/messages";

const model = new ChatOpenAI({
  model: "gpt-4o-mini",
  temperature: 0,
});

async function main() {
  const response = await model.invoke([
    new HumanMessage("Write one sentence about retries in distributed systems."),
  ]);

  console.log(response.content);
}

main().catch(console.error);
  1. Add a reusable retry helper with exponential backoff and jitter. This keeps the policy explicit and works for any LangChain runnable, not just chat models.
type RetryOptions = {
  retries: number;
  baseDelayMs?: number;
};

function sleep(ms: number) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function withRetry<T>(
  fn: () => Promise<T>,
  { retries, baseDelayMs = 250 }: RetryOptions,
): Promise<T> {
  let lastError: unknown;

  for (let attempt = 0; attempt <= retries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;
      if (attempt === retries) break;

      const backoff = baseDelayMs * Math.pow(2, attempt);
      const jitter = Math.floor(Math.random() * baseDelayMs);
      await sleep(backoff + jitter);
    }
  }

  throw lastError;
}
  1. Wrap the LangChain call with that helper. This is the pattern you want in production when a provider returns temporary failures like 429, 500, or network timeouts.
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage } from "@langchain/core/messages";

const model = new ChatOpenAI({
  model: "gpt-4o-mini",
  temperature: 0,
});

async function main() {
  const result = await withRetry(
    () =>
      model.invoke([
        new HumanMessage("Summarize retry logic in one short paragraph."),
      ]),
    { retries: 3, baseDelayMs: 300 },
  );

  console.log(result.content);
}

main().catch(console.error);
  1. If you want provider-native retries too, configure them on the model and keep your own wrapper for app-level control. This gives you two layers: SDK-level retries for transport issues and your wrapper for workflow-specific handling.
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage } from "@langchain/core/messages";

const model = new ChatOpenAI({
  model: "gpt-4o-mini",
  temperature: 0,
  maxRetries: 2,
});

async function main() {
  const result = await withRetry(
    () =>
      model.invoke([
        new HumanMessage("Give me a concise definition of idempotency."),
      ]),
    { retries: 2, baseDelayMs: 500 },
  );

  console.log(result.content);
}

main().catch(console.error);
  1. Make the retry policy selective instead of retrying everything. You usually want to retry rate limits and transient network errors, but not bad prompts or validation errors.
function isRetryable(error: unknown): boolean {
  if (!(error instanceof Error)) return false;

  const message = error.message.toLowerCase();
  return (
    message.includes("429") ||
    message.includes("rate limit") ||
    message.includes("timeout") ||
    message.includes("fetch failed") ||
    message.includes("503") ||
    message.includes("500")
  );
}

async function withSelectiveRetry<T>(
  fn: () => Promise<T>,
  retries = 3,
): Promise<T> {
  let lastError: unknown;

  for (let attempt = 0; attempt <= retries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;
      if (attempt === retries || !isRetryable(error)) break;
      await sleep(250 * Math.pow(2, attempt));
    }
  }

  throw lastError;
}
  1. Use the selective wrapper in your app entrypoint and keep the call site clean. That way your chain code stays readable while operational concerns live in one place.
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage } from "@langchain/core/messages";

const model = new ChatOpenAI({
model: "gpt-4o-mini",
temperature: 0,
});

async function main() {
const response = await withSelectiveRetry(() =>
model.invoke([new HumanMessage("List three causes of transient API failures.")]),
3);

console.log(response.content);
}

main().catch((error) => {
console.error("Request failed after retries:", error);
process.exitCode = 1;
});

Testing It

Run the script normally first and confirm you get a response from the model. Then temporarily break your API key or point to an invalid endpoint so you can see the retry loop kick in.

For a cleaner test, lower retries to 1 and add logging inside the helper so you can observe each attempt and delay. In production code, log the attempt count, error class, status code if available, and total elapsed time.

If you're using this in an agent flow, test both success paths and failure paths around tool calls as well as LLM calls. The common mistake is retrying only the top-level prompt while leaving tool failures unhandled.

Next Steps

  • Add structured logging and metrics for retry attempts, final failures, and latency impact.
  • Replace message-string matching with provider-specific error inspection where available.
  • Move this pattern into a shared utility so every chain, tool call, and agent step uses the same policy.

Keep learning

By Cyprian Aarons, AI Consultant at Topiax.

Want the complete 8-step roadmap?

Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.

Get the Starter Kit

Related Guides