install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/claude-code/resilience-timeout-pattern" ~/.claude/skills/intense-visions-harness-engineering-resilience-timeout-pattern && rm -rf "$T"
manifest:
agents/skills/claude-code/resilience-timeout-pattern/SKILL.mdsource content
Timeout Pattern
Prevent resource exhaustion and hung requests with timeouts, AbortController, and deadline propagation
When to Use
- HTTP requests that could hang indefinitely due to network issues
- Database queries that could run longer than acceptable
- Any async operation that should not block forever
- Enforcing SLA response times across service boundaries
Instructions
- Set timeouts on every external call. Never rely on the default (which is often infinity).
- Use
withAbortController
for fetch requests.AbortSignal.timeout() - Set connection timeouts AND request timeouts separately — connection timeout is for establishing the connection; request timeout is for receiving the response.
- Propagate deadlines across service boundaries — if service A has 5s remaining, service B should get less than 5s.
- Clean up resources (connections, streams, file handles) when a timeout fires.
- Return a meaningful error on timeout so callers can retry or fall back.
// utils/timeout.ts export async function withTimeout<T>( fn: (signal: AbortSignal) => Promise<T>, timeoutMs: number, errorMessage = 'Operation timed out' ): Promise<T> { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeoutMs); try { const result = await fn(controller.signal); return result; } catch (error) { if (controller.signal.aborted) { throw new TimeoutError(errorMessage, timeoutMs); } throw error; } finally { clearTimeout(timeoutId); } } export class TimeoutError extends Error { constructor( message: string, public readonly timeoutMs: number ) { super(message); this.name = 'TimeoutError'; } }
// Usage with fetch import { withTimeout } from './utils/timeout'; const user = await withTimeout( (signal) => fetch('/api/users/123', { signal }).then((r) => r.json()), 5000, 'User fetch timed out' ); // Node.js built-in (Node 18+) const response = await fetch('/api/data', { signal: AbortSignal.timeout(5000), });
// Deadline propagation across services async function handleRequest(req: Request) { const deadline = Date.now() + 10000; // 10s total budget const user = await withTimeout( (signal) => userService.getUser(req.userId, signal), 3000 // 3s budget for user service ); const remaining = deadline - Date.now(); if (remaining <= 0) throw new TimeoutError('Request deadline exceeded', 10000); const orders = await withTimeout( (signal) => orderService.getOrders(user.id, signal), Math.min(remaining, 5000) // Remaining budget, capped at 5s ); return { user, orders }; }
Details
AbortController in Node.js: Supported natively since Node 16.
AbortSignal.timeout(ms) is available since Node 18 — it creates a signal that automatically aborts after the specified time.
Timeout layering: Set timeouts at multiple levels:
- HTTP client level:
signal, Axios timeout configfetch - Service call level:
wrapperwithTimeout - Route handler level: Express/Fastify request timeout
- Load balancer level: Nginx/ALB timeout
Common timeout values:
- Internal microservice calls: 1-5 seconds
- External API calls: 5-15 seconds
- Database queries: 5-30 seconds
- File uploads/downloads: 60-300 seconds
- Background jobs: application-specific
Database timeouts:
// PostgreSQL via node-postgres const pool = new Pool({ connectionTimeoutMillis: 5000, // Wait for connection query_timeout: 10000, // Query execution limit statement_timeout: 10000, // Server-side timeout });
Anti-patterns:
- No timeout at all (requests hang forever during outages)
- Timeout too short (causes false failures under normal load)
- Not cleaning up after timeout (leaks connections)
- Not propagating the abort signal to child operations
Source
https://nodejs.org/api/globals.html#class-abortcontroller
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.