How to fix TimeoutError: Operation timeout while promise pending in Node.js
This error occurs when an asynchronous operation takes longer than the specified timeout duration to complete. The promise remains in a pending state past its deadline, triggering a timeout rejection to prevent indefinite waiting.
Quick fix
Fixes TimeoutError: Operation timeout while promise pending
function timeout(ms, promise) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error(`TimeoutError: Operation timeout after ${ms}ms`));
}, ms);
promise
.then((value) => {
clearTimeout(timer);
resolve(value);
})
.catch((err) => {
clearTimeout(timer);
reject(err);
});
});
}
// Usage
try {
const result = await timeout(5000, fetch('https://api.example.com/data'));
console.log('Success:', result);
} catch (err) {
if (err.message.includes('TimeoutError')) {
console.error('Request timed out');
} else {
console.error('Other error:', err);
}
}- Implement Promise.race() timeout pattern
- Use AbortController for cancellable operations
- Increase timeout duration if operations are legitimately slow
- Add retry logic with exponential backoff
function timeout(ms, promise) {return new Promise((resolve, reject) => {const timer = setTimeout(() => {reject(new Error(`TimeoutError: Operation timeout after ${ms}ms`));}, ms);promise.then((value) => {clearTimeout(timer);resolve(value);}).catch((err) => {clearTimeout(timer);reject(err);});});}// Usagetry {const result = await timeout(5000, fetch('https://api.example.com/data'));console.log('Success:', result);} catch (err) {if (err.message.includes('TimeoutError')) {console.error('Request timed out');} else {console.error('Other error:', err);}}TimeoutError: Operation timeout while promise pending
What this error means
Promises in JavaScript/Node.js don't have built-in timeout mechanisms. When you implement a timeout wrapper around a promise (using Promise.race, AbortController, or timeout libraries), and the underlying operation doesn't resolve or reject within the specified time limit, a TimeoutError is thrown. This prevents your application from hanging indefinitely while waiting for slow network requests, database queries, or other long-running operations. The key insight is that the timeout doesn't cancel the underlying operation—it simply stops waiting for it and rejects the promise with a timeout error.
Typical symptoms
- TimeoutError thrown with message about operation timing out
- Application appears to hang waiting for async operations
- Network requests fail with timeout errors in logs
- Database queries exceed connection timeout limits
- API endpoints return 408 Request Timeout status codes
- Promise remains in pending state indefinitely without timeout wrapper
- External service calls fail after configured timeout duration
Common causes
- Network latency or slow external API responses exceeding timeout limit
- Database queries taking too long due to missing indexes or large datasets
- Timeout configured too aggressively for the operation's typical duration
- DNS resolution delays causing initial connection timeouts
- Connection pool exhaustion delaying operation start time
- Deadlocks or blocking operations in database transactions
- Insufficient server resources causing processing delays
- Promises that never resolve or reject (missing return statements or error handling)
How to fix "TimeoutError: Operation timeout while promise pending"
Create a timeout wrapper using Promise.race() to limit how long you wait for an operation:
function timeout(ms, promise) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error(`TimeoutError: Operation timeout after ${ms}ms`));
}, ms);
promise
.then((value) => {
clearTimeout(timer);
resolve(value);
})
.catch((err) => {
clearTimeout(timer);
reject(err);
});
});
}
// Usage
try {
const result = await timeout(5000, fetch('https://api.example.com/data'));
console.log('Success:', result);
} catch (err) {
if (err.message.includes('TimeoutError')) {
console.error('Request timed out');
} else {
console.error('Other error:', err);
}
}Always clear the timeout when the promise settles to prevent memory leaks.
Modern Node.js (v16.14+) supports AbortSignal.timeout() for cleaner timeout handling:
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
try {
const response = await fetch('https://api.example.com/data', {
signal: controller.signal,
});
clearTimeout(timeoutId);
const data = await response.json();
console.log('Success:', data);
} catch (err) {
if (err.name === 'AbortError') {
console.error('Request was aborted due to timeout');
} else {
console.error('Fetch failed:', err);
}
}Or use the static timeout method:
try {
const response = await fetch('https://api.example.com/data', {
signal: AbortSignal.timeout(5000),
});
const data = await response.json();
} catch (err) {
if (err.name === 'TimeoutError') {
console.error('Request timed out after 5 seconds');
}
}If timeout errors occur for operations that need more time, adjust the timeout value:
// Before: Too aggressive
await timeout(1000, heavyDatabaseQuery());
// After: More realistic for the operation
await timeout(10000, heavyDatabaseQuery());For database queries, configure connection timeouts appropriately:
const pool = new Pool({
connectionTimeoutMillis: 10000, // 10 seconds to establish connection
query_timeout: 30000, // 30 seconds for query execution
statement_timeout: 25000, // 25 seconds for statement execution
});Balance timeout values based on expected operation duration and user experience requirements.
Implement automatic retries for timeout errors that might succeed on subsequent attempts:
async function fetchWithRetry(url, options = {}, maxRetries = 3) {
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
const response = await fetch(url, {
...options,
signal: controller.signal,
});
clearTimeout(timeoutId);
return response;
} catch (err) {
lastError = err;
if (err.name === 'AbortError' && i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000; // Exponential backoff
console.log(`Retry ${i + 1} after ${delay}ms`);
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}
throw err;
}
}
throw lastError;
}Add timing instrumentation to identify which operations are causing timeouts:
async function timedOperation(name, operation, timeoutMs = 5000) {
const startTime = Date.now();
try {
const result = await timeout(timeoutMs, operation);
const duration = Date.now() - startTime;
console.log(`${name} completed in ${duration}ms`);
return result;
} catch (err) {
const duration = Date.now() - startTime;
console.error(`${name} failed after ${duration}ms:`, err.message);
if (err.message.includes('TimeoutError')) {
// Log to monitoring service
console.error('TIMEOUT:', {
operation: name,
duration,
configuredTimeout: timeoutMs,
timestamp: new Date().toISOString(),
});
}
throw err;
}
}
// Usage
await timedOperation('Database query', () => db.query('SELECT * FROM users'));Install and use the p-timeout library for production-ready timeout management:
npm install p-timeoutimport pTimeout from 'p-timeout';
try {
const result = await pTimeout(
fetch('https://api.example.com/data'),
{
milliseconds: 5000,
message: 'API request timed out',
fallback: () => {
// Optional fallback value when timeout occurs
return { cached: true, data: cachedData };
},
}
);
} catch (err) {
if (err.name === 'TimeoutError') {
console.error('Request exceeded 5 second limit');
}
}The library handles edge cases and provides clear error messages.
Advanced notes
Important distinction: Adding a timeout to a promise does NOT terminate or cancel the underlying operation—it only stops waiting for it. The operation continues running in the background. For truly cancellable operations, you must use AbortController and ensure the underlying API supports abortion (like fetch, most HTTP libraries, and some database drivers).
When using Promise.race() for timeouts, both promises continue executing. The losing promise's result is discarded, but its side effects still occur. This can lead to unexpected behavior if you're not careful with resource cleanup.
For Node.js v15+, the timers/promises module provides native promise-based setTimeout:
import { setTimeout } from 'timers/promises';
try {
await setTimeout(5000, 'timeout', { signal: AbortSignal.timeout(3000) });
} catch (err) {
console.error('Aborted before completion');
}In microservices architectures, timeout errors often cascade. If Service A times out waiting for Service B, ensure Service A doesn't retry immediately—this can amplify load on Service B. Use circuit breakers (like opossum) to prevent cascading failures:
import CircuitBreaker from 'opossum';
const breaker = new CircuitBreaker(asyncOperation, {
timeout: 5000, // Timeout threshold
errorThresholdPercentage: 50,
resetTimeout: 30000, // Try again after 30s
});
breaker.fallback(() => ({ error: 'Service unavailable' }));For database operations, timeout errors may indicate missing indexes, N+1 query problems, or connection pool exhaustion. Use query explain plans and connection pool monitoring to diagnose the root cause rather than simply increasing timeout values.
Consider returning 408 Request Timeout HTTP status codes to clients when upstream operations time out, rather than generic 500 errors. This provides better semantic meaning and helps with debugging.
Frequently asked questions
- What does "TimeoutError: Operation timeout while promise pending" mean?
- js don't have built-in timeout mechanisms. race, AbortController, or timeout libraries), and the underlying operation doesn't resolve or reject within the specified time limit, a TimeoutError is thrown. This prevents your application from hanging indefinitely while waiting for slow network requests, database queries, or other long-running operations.
- How do I fix "TimeoutError: Operation timeout while promise pending"?
- This error occurs when an asynchronous operation takes longer than the specified timeout duration to complete. The promise remains in a pending state past its deadline, triggering a timeout rejection to prevent indefinite waiting.
- What causes "TimeoutError: Operation timeout while promise pending"?
- Network latency or slow external API responses exceeding timeout limit Database queries taking too long due to missing indexes or large datasets Timeout configured too aggressively for the operation's typical duration
- Is the "TimeoutError: Operation timeout while promise pending" error serious?
- It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.