agent reused one idempotency key for two invoices: second invoice silently dropped
Fixes dropped invoices caused by reusing one idempotency key across distinct operations. Use when a second invoice vanished or an API returned the first invoice for a different request. Not for retry-duplicate problems.
agent reused one idempotency key for two invoices: second invoice silently dropped
TL;DR
Give every distinct invoice its own idempotency key: derive the key from the invoice's unique content (customer + lines + date), never from a shared constant. If an invoice was silently dropped, check whether the key was already used, then reissue with a fresh unique key. One key means one logical operation; sharing it merges two invoices into one.
agent reused one idempotency key for two invoices: second invoice silently droppedFix it
- Derive the key from the invoice content. Hash customer id + line items + amounts + billing date, e.g.
inv-[customer_id]-[date]-[short-hash-of-lines]. Success check: two different invoices produce two different keys. - Never use a constant or run-scoped key. A key like
daily-invoice-runor a single UUID generated once per batch guarantees collisions. Success check: code review shows the key built inside the per-invoice loop, not outside it. - Detect a silent drop. If the API returns an invoice whose lines don't match your request, you reused a key; log the mismatch and reissue. Success check: a mismatch in staging raises an alert instead of passing silently.
- Recover the dropped invoice. Confirm the first invoice is correct and paid/due as intended, then create the second invoice with a fresh unique key. Success check: both customers have their own invoice with matching line items.
When this applies
- Batch invoice jobs where one request's response came back for a different invoice.
- An invoice is missing and the API log shows a 200 with a different invoice id.
When it doesn't
- Retries created duplicates; that is the opposite bug (a new key per retry), handled separately.
- The invoice failed validation; check the error response, not the key.
Compatibility
- Pattern: agent batch-invoicing on Stripe, Chargebee, Xero, QuickBooks, or any API honoring idempotency keys.
- Keys are provider-opaque strings; the derivation scheme is yours to design.
Variant: monthly billing reused last month's key
The key included the customer but not the period. Same fix: the billing date/period must be part of the key material.
Variant: two agents shared a key namespace
Both agents generated keys like invoice-1. Same fix plus a per-agent or per-job prefix in the key.
Why it happens
Idempotency keys dedupe by equality: same key means "replay the first request." When an agent reuses a key for a genuinely different invoice, the provider correctly (from its view) returns the original invoice, and the second one never exists.
Edge cases
- Retrying the SAME invoice must reuse its key; only distinct invoices get distinct keys. Keep the derivation deterministic so retries reproduce the same key.
- Don't put secrets or PII in the key; a hash of the content is enough.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_ROgbQlzQ6NBQv0QeJILHcA