# How Instagit Handles Empty API Responses with Retry Logic

> Instagit handles empty API responses by retrying requests up to three times with exponential backoff, ensuring robust data fetching. Learn more about its error handling.

- Repository: [Instalabs AI/instagit](https://github.com/instalabsai/instagit)
- Tags: how-to-guide
- Published: 2026-02-19

---

**Instagit treats empty successful API responses as retryable errors, automatically reattempting the request up to three times with exponential backoff before surfacing a final error.**

When streaming analysis results from the Instagit API (`instalabsAI/instagit`), transient issues can cause the server to return HTTP 200 with no payload. Rather than failing immediately, the client implements a resilient retry strategy that distinguishes between permanent errors and temporary empty responses. This article examines the implementation details of Instagit's empty API response retry logic.

## The Core Retry Architecture

Instagit's retry mechanism spans two primary files. The [`src/retry.ts`](https://github.com/instalabsAI/instagit/blob/main/src/retry.ts) module defines the retry policy constants and delay calculations, while [`src/api.ts`](https://github.com/instalabsAI/instagit/blob/main/src/api.ts) houses the `analyzeRepoStreaming` function that orchestrates the request loop and empty-body detection.

The system recognizes three categories of retryable conditions: transport-level network errors, specific HTTP status codes (303, 502, 503, 504), and successful responses with empty bodies.

## How Empty Responses Are Detected

### Security Validation First

Before evaluating whether a response is truly empty, Instagit runs a security validation check via `isSecurityRejection` at `src/api.ts#L73`. This ensures that permanent security blocks are not mistaken for transient empty responses that warrant retry.

### Empty Body Identification

When the API returns a successful status (200-299), `analyzeRepoStreaming` parses the Server-Sent Events (SSE) stream and accumulates the output into `collectedText`. If this variable remains empty after the stream completes, the code explicitly treats this condition as retryable at `src/api.ts#L78`.

## The Retry Loop in Detail

### Maximum Attempts

The global constant `MAX_RETRIES` in `src/retry.ts#L7` defaults to `3`, meaning the client makes up to four total attempts (initial request plus three retries). The `for` loop in `analyzeRepoStreaming` iterates from `attempt = 0` up to this ceiling at `src/api.ts#L87`.

### Exponential Backoff Strategy

Between retries, Instagit implements exponential backoff via `getRetryDelay` in `src/retry.ts#L35`. The function calculates delay using the formula `RETRY_BASE_DELAY * (2 ^ attempt)`, where `RETRY_BASE_DELAY` equals 5 seconds.

This produces the following timing pattern:
- Attempt 0 (initial): Immediate execution
- Attempt 1: 5 second delay
- Attempt 2: 10 second delay
- Attempt 3: 20 second delay

### Progress Callbacks

During the retry cycle, Instagit surfaces status updates through the optional `progressCallback` parameter. When an empty response triggers a retry, the client updates the progress tracker with a retry message at `src/api.ts#L81-L83`, allowing applications to display real-time feedback to users.

### Final Error Handling

If all retry attempts are exhausted and the response remains empty, `analyzeRepoStreaming` throws a definitive error with the message "Empty response after retries" at `src/api.ts#L86`.

## Code Examples

### Basic Usage with Progress Callback

```typescript
import { analyzeRepoStreaming } from "./src/api.js";

async function run() {
  try {
    const result = await analyzeRepoStreaming({
      repo: "instalabsAI/instagit",
      prompt: "Summarize the repository",
      progressCallback: async (msg) => console.log("[progress]", msg),
    });

    console.log("✅ Finished:", result.text);
  } catch (e) {
    console.error("❌ Analysis failed:", e);
  }
}

run();

```

If the API returns an empty body, you will see progress messages like `Retrying... (attempt 2/4)` until the retry limit is reached.

### Simulating an Empty Response for Testing

```typescript
import { analyzeRepoStreaming } from "./src/api.js";
import { vi } from "vitest";

// Mock fetch to return a 200 with an empty SSE stream
global.fetch = vi.fn().mockImplementation(() =>
  Promise.resolve({
    ok: true,
    body: new ReadableStream({
      start(controller) {
        // Immediately close the stream → empty text
        controller.close();
      },
    }),
  })
);

await analyzeRepoStreaming({
  repo: "my/repo",
  prompt: "test",
  progressCallback: async (msg) => console.log(msg),
});

```

Running this snippet triggers the empty-body retry path, demonstrating the exponential backoff logic in action.

## Key Files and Their Roles

- **[`src/api.ts`](https://github.com/instalabsAI/instagit/blob/main/src/api.ts)** – Contains the `analyzeRepoStreaming` function that implements the retry loop and empty-body detection logic.
- **[`src/retry.ts`](https://github.com/instalabsAI/instagit/blob/main/src/retry.ts)** – Defines retry constants (`MAX_RETRIES`, `RETRY_BASE_DELAY`), status code classifications (`RETRYABLE_STATUS_CODES`), and the `getRetryDelay` exponential backoff calculator.
- **[`src/kitt.ts`](https://github.com/instalabsAI/instagit/blob/main/src/kitt.ts)** – Provides progress-tracking utilities used by the retry loop to surface status updates via callbacks.
- **[`src/token.ts`](https://github.com/instalabsAI/instagit/blob/main/src/token.ts)** – Handles authentication token refresh, which indirectly influences retry flow when auth errors require token renewal before subsequent attempts.

## Summary

- Instagit treats empty successful API responses as transient failures rather than final results, triggering automatic retries.
- The retry mechanism allows up to three retry attempts (`MAX_RETRIES = 3`) with exponential backoff delays starting at 5 seconds.
- Security validation (`isSecurityRejection`) runs before empty-body detection to prevent retrying permanent security blocks.
- Progress callbacks notify applications of retry attempts in real-time, improving UX during transient failures.
- After exhausting all retries, the client throws a definitive "Empty response after retries" error.

## Frequently Asked Questions

### How many times will Instagit retry an empty API response?

Instagit will retry up to three times by default. The `MAX_RETRIES` constant in [`src/retry.ts`](https://github.com/instalabsAI/instagit/blob/main/src/retry.ts) is set to `3`, meaning the client makes one initial request plus three additional retry attempts before failing permanently.

### What is the delay between retry attempts for empty responses?

The delay follows an exponential backoff pattern starting at 5 seconds. The `getRetryDelay` function in [`src/retry.ts`](https://github.com/instalabsAI/instagit/blob/main/src/retry.ts) calculates the wait time as `RETRY_BASE_DELAY * (2 ^ attempt)`, resulting in delays of 5 seconds, 10 seconds, and 20 seconds for successive retries.

### Does Instagit distinguish between empty responses and security blocks?

Yes, Instagit runs a security validation check via `isSecurityRejection` before treating a response as empty. This check at `src/api.ts#L73` ensures that permanent security rejections are not mistaken for transient empty responses that should trigger retries.

### Can I monitor when Instagit is retrying due to an empty response?

Yes, you can monitor retry attempts by providing a `progressCallback` function when calling `analyzeRepoStreaming`. The client updates the progress tracker with retry messages at `src/api.ts#L81-L83`, allowing your application to display real-time status updates to users during the retry cycle.