Threads · 12 / 18 · Sep 7, 2026 · Apache-2.0
The key identifies the effect, not the occasion
Deriving the idempotency key from the turn, the request or the run looks reasonable and silently deletes the second legitimate action of the same turn. And there is a second decision nobody writes down — where the key is checked — that duplicates instead of losing.
Two independent decisions, and both are usually made by accident:
| Get it wrong and… | It looks like | |
|---|---|---|
| What the key identifies — the occasion, or the effect | work disappears | the system says "already handled" |
| Where it is checked — before the effect, or after | work duplicates | the audit trail says it happened once |
Axis 1 · what the key identifies
An agent decides, in one turn, to charge a subscription and an overage. Two effects, one occasion:
recordThenCharge(p, l, suscripcion, keyOfOccasion(turn));
recordThenCharge(p, l, excedente, keyOfOccasion(turn));
expect(p.charged).toHaveLength(1); // the overage is gone
Nothing errored. The second call returned already handled, which is exactly what idempotency is supposed to say. You are now under-billing, and the system is reporting that it works.
Keyed by the effect, both survive — and a genuine repeat is still stopped:
keyOfEffect({ customer: "ana", amount: "40.00", reason: "subscription" })
keyOfEffect({ customer: "ana", amount: "12.00", reason: "overage" }) // a different key
Axis 2 · where it is checked
This is the one that survives design review, because everything about it reads correctly: the key is right, the unique index is real, and the retry is refused.
chargeThenRecord(p, l, suscripcion, key);
chargeThenRecord(p, l, suscripcion, key); // the client timed out and tried again
expect(p.charged).toHaveLength(2); // charged twice
expect(l.rows).toHaveLength(1); // recorded once
The check runs after the money moved. So the customer is charged twice and the audit trail says it happened once — which means the duplicate cannot be found later either. It is the worst combination in the repository and the one that looks most correct while you are reading it.
The two axes are independent. A review that only asks "what is your idempotency key?" passes this code, and this code double-charges on every retry.
And a timeout is not an exotic case
for (let i = 0; i < 3; i++) recordThenCharge(p, l, suscripcion, key);
expect(p.charged).toHaveLength(1);
Any HTTP client retries when it does not get a response. Idempotency is not a feature for careful callers — it is what makes ordinary callers safe, and the traffic that exercises it arrives on day one.
Where this comes from
Both halves are from niiko, and neither was found by reasoning about it.
The first was caught while designing the agent's charge path: the key was going to come from the turn, and the question "what if it wants to do two things?" had no good answer.
The second was found months later by an adversarial audit of a public API design — the gate computed the key
correctly, wrote it to an audited table with a unique index, and did both after calling the action. A
retry would have published the same post twice, answered ok twice, and left one row behind.
License
Apache-2.0 — see LICENSE. This is a demonstration, not a package. Copy what you need.
Built by Vorluno — a software studio from Panamá.
// next threadThe model may supply search terms, never identifiers