Timeout

Infrastructure Jan 9, 2026 JAVASCRIPT
resilience performance error-handling reliability configuration

Definition

Timeout is a configuration that limits how long a client will wait for an operation (request, connection, read) to complete before aborting it. Timeouts prevent indefinite waiting when services are unresponsive, protecting against resource exhaustion, cascading failures, and poor user experience. Different timeout types apply to different stages of network communication.

Timeout types:

  1. Connection Timeout - Max time to establish TCP connection
  2. Request Timeout - Max time for entire request/response cycle
  3. Read Timeout - Max time waiting for data after connection established
  4. Idle Timeout - Max time connection can remain idle before closing
  5. Keep-Alive Timeout - Max time connection stays open for reuse

Example

Stripe API Timeout Strategy:

Stripe recommends different timeouts for different operations:

// Quick operations: 10s timeout
const payment = await stripe.paymentIntents.create({
  amount: 1000,
  currency: 'usd'
}, {
  timeout: 10000 // 10 seconds
});

// Long-running operations: 80s timeout
const payout = await stripe.payouts.create({
  amount: 10000,
  currency: 'usd'
}, {
  timeout: 80000 // 80 seconds for bank transfers
});

// Webhook event retrieval: 30s timeout
const event = await stripe.events.retrieve('evt_123', {
  timeout: 30000
});

Real-world behavior:

  • Connection timeout: 5s (establish TCP)
  • Request timeout: 30s (complete API call)
  • If timeout exceeded → Retry with exponential backoff (max 3 attempts)
  • Idempotency keys prevent duplicate operations on retry

Code Example

class TimeoutClient {
  constructor(config = {}) {
    this.connectionTimeout = config.connectionTimeout || 5000;  // 5s
    this.requestTimeout = config.requestTimeout || 30000;      // 30s
    this.readTimeout = config.readTimeout || 20000;            // 20s
  }

  async request(url, options = {}) {
    // Create AbortController for timeout management
    const controller = new AbortController();
    const timeoutId = setTimeout(
      () => controller.abort(),
      options.timeout || this.requestTimeout
    );

    try {
      const response = await fetch(url, {
        ...options,
        signal: controller.signal,
        // Connection timeout via custom agent (Node.js)
        agent: this.createAgent()
      });

      clearTimeout(timeoutId);
      return await response.json();

    } catch (error) {
      clearTimeout(timeoutId);

      if (error.name === 'AbortError') {
        throw new TimeoutError(
          `Request timeout after ${options.timeout || this.requestTimeout}ms`,
          url
        );
      }

      throw error;
    }
  }

  createAgent() {
    // Node.js: Configure connection timeout
    return new http.Agent({
      timeout: this.connectionTimeout,
      keepAlive: true,
      keepAliveMsecs: 30000 // Keep-alive timeout
    });
  }
}

// Custom timeout error
class TimeoutError extends Error {
  constructor(message, url) {
    super(message);
    this.name = 'TimeoutError';
    this.url = url;
    this.retryable = true;
  }
}

// Usage with cascading timeouts
async function fetchWithCascadingTimeouts(url) {
  const client = new TimeoutClient({
    connectionTimeout: 5000,  // 5s to connect
    requestTimeout: 30000,    // 30s total
    readTimeout: 20000        // 20s to read response
  });

  try {
    // First attempt: normal timeout
    return await client.request(url);
  } catch (error) {
    if (error instanceof TimeoutError) {
      console.warn(`First attempt timed out, retrying with extended timeout`);
      
      // Retry with longer timeout
      return await client.request(url, { timeout: 60000 });
    }
    throw error;
  }
}

// Timeout with Promise.race pattern
async function requestWithTimeout(url, timeoutMs) {
  const timeout = new Promise((_, reject) =>
    setTimeout(() => reject(new TimeoutError(`Timeout after ${timeoutMs}ms`)), timeoutMs)
  );

  const request = fetch(url);

  return Promise.race([request, timeout]);
}

// Usage
try {
  const data = await requestWithTimeout('https://api.example.com/slow', 5000);
  console.log('Success:', data);
} catch (error) {
  if (error instanceof TimeoutError) {
    console.error('Request timed out');
    // Implement fallback or retry logic
  }
}

Diagram

sequenceDiagram
    participant Client
    participant Proxy
    participant API
    participant DB

    Note over Client: Connection Timeout: 5s
Request Timeout: 30s Client->>Proxy: TCP SYN activate Proxy Note over Proxy: Connection timeout starts Proxy->>API: Forward connection activate API API-->>Proxy: SYN-ACK Proxy-->>Client: Connected (2s elapsed) deactivate Proxy Note over Client: Request timeout starts Client->>API: GET /data API->>DB: Query (slow) activate DB Note over DB: Query takes 25s DB-->>API: Result (25s) deactivate DB API-->>Client: 200 OK (27s total) Note over Client: Success within 30s timeout Client->>API: GET /heavy-query API->>DB: Complex query activate DB Note over DB: Query takes 35s rect rgb(255, 200, 200) Note over Client: Request timeout (30s) exceeded Client->>Client: Abort request end DB-->>API: Result (too late) deactivate DB API-->>Client: 200 OK (ignored)

Best Practices

1. Set Timeouts at Every Layer Configure connection, request, and read timeouts. Don’t rely on defaults which may be too long (or infinite).

2. Use Different Timeouts for Different Operations Quick lookups: 5-10s. Complex queries: 30-60s. File uploads: 5 minutes. Webhooks: 3-5s.

3. Connection Timeout < Read Timeout < Request Timeout Typical hierarchy: Connection (5s) < Read (20s) < Request (30s).

4. Always Clean Up After Timeouts Use AbortController to cancel in-flight requests. Clear timeout handlers to prevent memory leaks.

5. Make Timeouts Configurable Allow users to adjust timeouts via environment variables or config files for different deployment environments.

6. Retry After Timeout (With Backoff) Timeouts often indicate transient issues. Retry with exponential backoff (but not indefinitely).

7. Log Timeout Events Record which operations timeout most frequently to identify bottlenecks and optimize services.

8. Use Circuit Breakers After N consecutive timeouts, stop sending requests to failing services for a cooldown period.

9. Return Partial Results on Timeout When possible, return cached or partial data instead of failing completely.

10. Monitor Timeout Rates Track timeout percentage per endpoint. Alert when thresholds exceeded (e.g., >5% timeout rate).

Common Mistakes

1. No Timeout Set (Infinite Wait) Without timeouts, clients can hang indefinitely on unresponsive services, exhausting connection pools.

2. Timeout Too Short Setting a 1s timeout for operations that legitimately take 5s causes false failures and wasted retries.

3. Timeout Too Long A 5-minute timeout on a user-facing request creates terrible UX. Use 10-30s for interactive operations.

4. Same Timeout for All Operations Using a single global timeout (e.g., 30s) ignores that file uploads need longer timeouts than quick lookups.

5. Not Cleaning Up After Timeout Forgetting to clear timeout handlers or abort requests causes memory leaks and wasted server resources.

6. Retrying Immediately After Timeout Retrying without backoff can worsen overload. Always use exponential backoff after timeouts.

7. No Monitoring of Timeout Rates High timeout rates indicate systemic issues (slow DB, overloaded service) that won’t be discovered without monitoring.

8. Timeout on Non-Idempotent Operations Without Keys Timing out a POST request and retrying without idempotency keys can duplicate operations (double charging users).

Standards & RFCs