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/codex/resilience-idempotency" ~/.claude/skills/intense-visions-harness-engineering-resilience-idempotency-442960 && rm -rf "$T"
manifest:
agents/skills/codex/resilience-idempotency/SKILL.mdsource content
Idempotency Pattern
Ensure safe retries by making operations produce the same result regardless of how many times they execute
When to Use
- API endpoints that create resources (POST) need to be safely retryable
- Payment processing where duplicate charges are unacceptable
- Message consumers that may receive the same message twice (at-least-once delivery)
- Any operation where network failures could cause the client to retry without knowing if the first request succeeded
Instructions
- Accept an
header on non-idempotent endpoints (POST, PATCH). The client generates a unique key per logical operation.Idempotency-Key - Before processing, check if the key has been seen. If yes, return the stored response.
- Process the request and store the response keyed by the idempotency key.
- Set a TTL on stored responses (24-48 hours). After expiry, the key can be reused.
- Handle concurrent requests with the same key — use a lock to prevent double processing.
- For message queues, use a message deduplication store keyed by message ID.
// middleware/idempotency.ts interface StoredResponse { statusCode: number; body: unknown; headers: Record<string, string>; createdAt: number; } export class IdempotencyStore { constructor( private redis: Redis, private ttlSeconds: number = 86400 // 24 hours ) {} async check(key: string): Promise<StoredResponse | null> { const data = await this.redis.get(`idem:${key}`); return data ? JSON.parse(data) : null; } async lock(key: string): Promise<boolean> { // Set NX — only succeeds if key does not exist const result = await this.redis.set(`idem:lock:${key}`, '1', 'EX', 60, 'NX'); return result === 'OK'; } async store(key: string, response: StoredResponse): Promise<void> { await this.redis.setex(`idem:${key}`, this.ttlSeconds, JSON.stringify(response)); await this.redis.del(`idem:lock:${key}`); } async unlock(key: string): Promise<void> { await this.redis.del(`idem:lock:${key}`); } } // Express middleware export function idempotencyMiddleware(store: IdempotencyStore) { return async (req: Request, res: Response, next: NextFunction) => { const key = req.headers['idempotency-key'] as string; if (!key) return next(); // No key = no idempotency protection // Check for cached response const cached = await store.check(key); if (cached) { Object.entries(cached.headers).forEach(([k, v]) => res.setHeader(k, v)); return res.status(cached.statusCode).json(cached.body); } // Acquire lock to prevent concurrent processing const locked = await store.lock(key); if (!locked) { return res.status(409).json({ error: 'Request is being processed' }); } // Intercept the response to store it const originalJson = res.json.bind(res); res.json = (body: unknown) => { store.store(key, { statusCode: res.statusCode, body, headers: { 'content-type': 'application/json' }, createdAt: Date.now(), }); return originalJson(body); }; next(); }; }
// Client-side usage const idempotencyKey = crypto.randomUUID(); const response = await fetch('/api/payments', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Idempotency-Key': idempotencyKey, }, body: JSON.stringify({ amount: 1000, currency: 'usd' }), }); // Safe to retry with the same key — will get the same response
Details
Naturally idempotent operations: GET, PUT, DELETE are idempotent by HTTP semantics.
PUT /users/123 { name: "Alice" } always produces the same result. POST is not naturally idempotent — creating a resource twice creates two resources.
Database-level idempotency: Use unique constraints and upserts:
-- Instead of INSERT (which fails on duplicate) INSERT INTO orders (idempotency_key, user_id, amount) VALUES ($1, $2, $3) ON CONFLICT (idempotency_key) DO NOTHING RETURNING *;
Message deduplication:
async function processMessage(message: QueueMessage): Promise<void> { const messageId = message.id; const processed = await redis.set(`msg:${messageId}`, '1', 'EX', 3600, 'NX'); if (!processed) { console.log(`Duplicate message ${messageId}, skipping`); return; } await handleMessage(message.body); }
Key generation strategies:
- Client-generated UUID (most common for APIs)
- Hash of request body (deterministic but risks collision if body legitimately repeats)
- Composite key (userId + operation + timestamp rounded to minute)
Edge cases:
- Request succeeds but response is lost (client retries, gets cached success response — correct)
- Request fails (do not cache the error — let the client retry with the same key)
- Concurrent requests with the same key (lock prevents double processing)
Source
https://stripe.com/docs/api/idempotent_requests
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.