When your task depends on an external system, you cannot control response speed. A payment processor might take 2 seconds or 30 seconds. A file converter might need 5 minutes. A human approver might need 3 days. Without proper timeout configuration, your tasks either fail prematurely or hang with no resolution.
This guide covers how to set timeouts at each level and what to do when they fire.
Step 1: Understand the Two Types of Timeouts
AsyncQueue provides two distinct timeout mechanisms:
1. HTTP Timeout (timeout) - How long to wait for your target URL to respond to the HTTP request. If the server does not reply within this window, the request fails and may be retried.
2. Wait Timeout (maxWaitTime) - How long a task can remain in the waiting state (for wait-for-signal tasks). If no signal arrives within this window, the task transitions to timeout status.
Task Created │ ├── HTTP Timeout (timeout: 30s) │ Governs: How long the HTTP call to targetUrl can take │ On expire: Request fails, retry if attempts remain │ └── Wait Timeout (maxWaitTime: 3600s) Governs: How long the task can sit in "waiting" state On expire: Task moves to "timeout", onComplete firesThese mechanisms operate independently. A task can survive the HTTP timeout (callback succeeds) but later hit the wait timeout (no signal arrives).
Step 2: Set HTTP Timeouts for Callback Execution
The timeout field controls how long the HTTP request to your target URL can take before the system aborts the connection.
// Fast internal service - tight timeoutawait aq.tasks.create({ targetUrl: 'https://your-app.com/api/quick-check', timeout: 5, // 5 seconds maxRetries: 3,});
// Slow external API - generous timeoutawait aq.tasks.create({ targetUrl: 'https://slow-api.example.com/process', timeout: 120, // 2 minutes maxRetries: 2,});What happens when the HTTP timeout fires:
- The HTTP connection is aborted
- The task counts this as a failed attempt
- If retries remain, the task is re-queued with exponential backoff
- If all retries are exhausted, the task moves to
failedstatus
Tip: Set your timeout slightly higher than your external service’s expected response time. If the service typically responds in 10 seconds, use
timeout: 15to avoid false failures during slow periods.
Step 3: Set Wait Timeouts for Signal-Based Tasks
For tasks with waitForSignal: true, the maxWaitTime field controls how long the task can remain in the waiting state.
// Payment confirmation - wait up to 1 hourconst { task, signalToken } = await aq.tasks.create({ targetUrl: 'https://your-app.com/api/init-payment', waitForSignal: true, maxWaitTime: 3600, // 1 hour timeout: 30, // HTTP timeout for the initial callback});
// Document approval - wait up to 7 daysconst { task, signalToken } = await aq.tasks.create({ targetUrl: 'https://your-app.com/api/request-approval', waitForSignal: true, maxWaitTime: 604800, // 7 days timeout: 10,});What happens when the wait timeout fires:
- The task transitions from
waitingtotimeout - The signal token is invalidated (cannot be used afterward)
- The
onCompleteUrlwebhook fires with the timeout status - Your application can handle the timeout event
Step 4: Design Fallback Behavior for Timeouts
Timeouts are not errors - they are expected events. Design your application to handle them gracefully.
Pattern: onComplete webhook handles all terminal states
app.post('/api/task-webhook', async (req, res) => { const { task } = req.body;
switch (task.status) { case 'completed': await fulfillOrder(task.id, task.result); break;
case 'failed': await notifySupport('Task failed', task.id, task.error); break;
case 'timeout': // The external dependency did not respond in time await cancelPendingOrder(task.id); await notifyCustomer(task.id, 'Your request timed out. Please try again.'); break; }
res.json({ received: true });});Pattern: escalation on timeout
For critical workflows, escalate rather than fail silently:
case 'timeout': // Create a new task with longer timeout and alert the team await aq.tasks.create({ targetUrl: 'https://your-app.com/api/manual-review', payload: { originalTaskId: task.id, reason: 'timeout' }, }); await slackNotify('#ops', `Task ${task.id} timed out, escalated to manual review`); break;Step 5: Choose Timeout Values for Common Scenarios
| Use Case | HTTP Timeout | Wait Timeout | Notes |
|---|---|---|---|
| Internal API call | 5-10s | N/A | Your own services should respond fast |
| Payment processing | 30s | 3600s (1h) | Stripe/PayPal callbacks arrive fast, but customer action varies |
| File conversion | 120s | 1800s (30m) | Video/PDF processing time varies by file size |
| Email sending | 10s | N/A | Email APIs respond fast; delivery happens asynchronously |
| Human approval | 10s | 604800s (7d) | Initial request resolves fast; human response takes longer |
| Third-party API | 30-60s | N/A | Match the service’s documented timeout |
| Webhook relay | 10s | 86400s (24d) | Accept fast, wait for external callback |
General rules:
- Set HTTP timeout to 2-3x the expected response time
- Set wait timeout to the maximum acceptable delay for your business logic
- Handle the timeout event in every workflow - never assume tasks will complete