Skip to content

Stateful Key Busy

URI: https://docs.ankatech.co/errors/stateful-key-busy
HTTP Status: 409 Conflict

When you see this

A signing request named a stateful hash-based signature key — XMSS, XMSS^MT, LMS or HSS — while another request was reserving that key's next one-time signature. Signatures by one stateful key are serialised: each one commits its index as spent before it signs, and only one request at a time may do that. The wait for that reservation is bounded, and this request did not get it in time. Nothing was signed and no index was spent. The response carries a Retry-After header.

It is a refusal, not a statement about the key: the key is intact and can sign again as soon as the reservation ahead of it ends.

Common Causes

  • Concurrent signatures by one key. Several clients, or several threads of one client, sign with the same stateful key at once.
  • This instance's reservation capacity is in use. Each core-api instance runs a bounded number of reservations at once, across all stateful keys: by default a quarter of its database connection pool, at least one (five with the default pool of twenty). One tenant may hold at most half of that capacity by default, so another tenant's stateful keys still get a reservation.
  • A slow key-protection backend. A reservation unwraps and re-wraps the key; when the HSM or Cloud KMS answers slowly, reservations last longer and the next request is more likely to wait. Each of those calls is bounded: a backend that does not answer within the bound — and a request that waited on such a call for its whole wait — is answered 503 key-protection-backend-unavailable instead of this refusal.

Response Example

{
  "type": "https://docs.ankatech.co/errors/stateful-key-busy",
  "title": "Stateful Key Busy",
  "status": 409,
  "detail": "Key 'my-lms-signing-key' is reserving a one-time signature for another request. The request may be retried.",
  "instance": "/api/v3/crypto/sign",
  "correlationId": "3f2b8c1e-6d4a-4f0b-9a7e-2c5d8e1f4a90",
  "timestamp": 1768472402,
  "extensions": {
    "code": "STATEFUL_KEY_BUSY"
  }
}

Header: Retry-After: 1 — the whole number of seconds after which the request may be retried, equal to the configured reservation wait bound. Every signing operation declares this header on its 409 in the published OpenAPI contract; it is sent only with stateful-key-busy and is absent on every other 409.

Content-Type: application/problem+json — the response follows RFC 9457 Problem Details.

Where it is answered

The same eight operations as Stateful Key Exhausted: POST /api/v3/crypto/sign, /resign, /sign-encrypt, their /stream/… forms, and the two PKCS#7-to-JOSE conversions. On a streaming operation the refusal is decided before any output exists.

How to Resolve

  1. Retry after the Retry-After interval. The request is safe to repeat: nothing was signed. A key use that admission already counted against a usage limit is not given back.
  2. Serialise signing by one key on the client side when you sign in bulk, or spread the load across several stateful keys: their reservations do not wait for each other.
  3. Do not read it as an outage. A database that cannot be reached, or that holds the key's row and does not complete the index write within its bound, is a 503 dependency-not-ready, not this refusal.

Related: Stateful Key Exhausted — the key can sign no more; Invalid Key State — the key's lifecycle changed under the request. Key semantics: Stateful Hash-Based Signature Keys.