Skip to content

A reused key with a different payload replays the first result: store a fingerprint #27

Description

@AlexeyShalaev

Measured on 0.2.0 with the Redis repository. After a charge of 1999 was recorded under key order-42, the same key came back with a different amount:

--- the same key again, with a different amount ---
requested amount 5, got back: charge_id='ch_1' amount=1999

Rule 1 on the agents page says the arguments are not part of the identity, so this is documented, and for a well-behaved client it never happens. It happens with a client that reuses keys by mistake (a key derived from the order id, then a second, different operation on the same order), and today the library answers such a request with a result for a different request and no signal that anything is off. Stripe rejects this with a 422 ("keys are for identical requests"), and I think that is the right shape.

Proposal: an optional fingerprint on the record. coordinate() takes fingerprint: str | None = None; the decorator takes a way to derive one from the call (a callable over the bound arguments, or a tuple of parameter names whose values are hashed after model_dump(mode="json") for Pydantic models). The fingerprint is stored on IdempotencyRecord (fingerprint: str | None, so existing records stay readable) and compared on a hit; a hit with a different fingerprint raises a new IdempotencyKeyReuseError carrying both fingerprints. That one must propagate through the coordinator rather than be swallowed: it is a caller error, not storage trouble, and rule 6 is about storage. No fingerprint supplied means today's behaviour, key only.

Script: measure.py in https://github.com/bedrock-python/bedrock-python.github.io/tree/docs/production-python-series/docs/blog/lab/2026-09-07-idempotency-keys. Related: the in-flight reservation issue filed alongside this one.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions