Skip to content

Troubleshooting

422 Unprocessable Entity when retrying what looks like the same request

Cause: the request body differs from what was originally associated with this idempotency key — even a whitespace or field-ordering difference in a raw string body would matter if your fingerprint were computed differently, though this package hashes the raw bytes Django received, so JSON key ordering from json.dumps on the client side does matter if it varies between attempts (e.g. a dict with non-deterministic key order serialized differently each time).

Fix: ensure your client serializes the retry body identically to the original (e.g. build the JSON string once and reuse it across attempts, rather than re-serializing a dict whose key order might vary), or generate a fresh idempotency key if the request is genuinely different.

409 Conflict that never resolves

Cause: either a genuinely very slow original request (increase LOCK_TTL_SECONDS if this is expected), or a worker that crashed while holding the lock — in which case the lock self-expires after LOCK_TTL_SECONDS and a subsequent retry will be allowed to proceed (see Architecture). If retries keep hitting 409 well past your configured LOCK_TTL_SECONDS, something is holding a new lock each time — check whether each retry is generating a new key rather than reusing one (a very common client bug).

Middleware raises the wrong kind of response / raw exception instead of JSON

If you're extending or overriding IdempotencyMiddleware and lost the try/except IdempotencyError wrapper, see Architecture — this package's exceptions subclass APIException, which only auto-converts to a response inside DRF's own view dispatch, not in plain Django middleware. If you write a custom middleware wrapper, keep that try/except (or call drf_idempotency.core.build_error_response yourself).

ImproperlyConfigured: Unknown key(s) in 'IDEMPOTENCY'

Cause: a typo in a setting name, or a key that doesn't exist. Compare against Settings — every key is case-sensitive.

django.db.utils.OperationalError: no such table: drf_idempotency_idempotencyrecord

Cause: using DatabaseBackend without running migrations.

Fix: ensure "drf_idempotency" is in INSTALLED_APPS and run python manage.py migrate.

ImportError: drf_idempotency.backends.redis requires the 'redis' package

Cause: RedisBackend configured without the redis package installed.

Fix: pip install drf-idempotency[redis].

A decorated view combined with global middleware returns an unexpected 409

This should not happen with the built-in re-entrancy guard — see Architecture. If you see this, check whether you're constructing a new request object somewhere between the middleware and the decorator (e.g. re-wrapping HttpRequest in a fresh Request instance) — the guard relies on a marker attribute persisting on the same request object across both layers. If your code creates a new request wrapper mid-stack, the marker won't be visible to the inner layer.

Response headers seem to be missing after replay

Only headers stored at complete() time are replayed; Content-Length, Connection, and Set-Cookie are deliberately excluded from what's stored (see Architecture) — the first two are recomputed correctly by Django regardless, and Set-Cookie is excluded as a deliberate safety choice (replaying a stale Set-Cookie value on every retry would be surprising and is rarely, if ever, desired). If you need a specific header preserved that isn't showing up, check it was actually present on the first response (the one that got cached), not just expected by convention.

Tests fail with "database is locked" using the database backend

This is a SQLite-specific limitation when running true concurrent (multi-threaded/multi-process) access against an in-memory database — see tests/test_concurrency.py's module docstring in this package's own repository for why its race-condition tests target the Redis backend instead. Use a real database (PostgreSQL/MySQL) or a file-based SQLite database with appropriate timeout settings if you need to test genuine multi-connection concurrency against DatabaseBackend specifically.