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:
- Connection Timeout - Max time to establish TCP connection
- Request Timeout - Max time for entire request/response cycle
- Read Timeout - Max time waiting for data after connection established
- Idle Timeout - Max time connection can remain idle before closing
- 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).