Skip to content

Server Capacity Exceeded

URI: https://docs.ankatech.co/errors/server-capacity-exceeded
HTTP Status: 503 Service Unavailable

When you see this

The instance that received the request is already holding as much of this kind of request as its memory allows. A buffered operation — one whose request body is read whole before it runs: the compact cryptographic operations under /api/v3/crypto/ (not the /stream/ ones) and the buffered PKCS#7 analysis and conversion — keeps a multiple of its body in memory while it works. Each instance admits these requests against a memory budget, weighting each one by the size of the body it declares, and refuses the excess before reading any of it. The request was well formed and nothing was processed; it can be sent again.

Two limits apply, and the response is the same for both:

  • the instance's budget for all buffered requests in flight;
  • one tenant's share of it, at most half, so one tenant cannot hold the whole budget.

An instance with nothing in flight always admits one request, so a request within its operation's size limit is never refused for its size alone.

Key and keystore imports, and keystore previews, are admitted against the same budget. The administration service, through which the Admin Console and the admin SDK import keys and keystores, answers its own Server Capacity Exceeded problem for them, with a Retry-After of 1 to 60 seconds. That problem carries the administration service's own detail, instance and extensions.requestId, and nothing of the cryptographic service's answer, so its shape differs from the example below.

How many requests one tenant can have in flight

About this many per instance, on the small sizing profile (buffered service heap 1152 MiB, streaming service heap 512 MiB, the default budget fraction 0.70), with request bodies at the default payload limit (4,000,000 bytes) and at the largest one (10,000,000 bytes), and no stream live on the streaming service:

Operation Served by One tenant, 4 MB One tenant, 10 MB
/api/v3/crypto/decrypt-verify buffered 2 1
/api/v3/crypto/sign-encrypt buffered 5 2
every other /api/v3/crypto/ operation buffered 3 1
/api/v3/migration/convert-pkcs7-to-jose streaming 1 1
/api/v3/migration/analyze-pkcs7 streaming 4 2

By sizing profile, for the three heaviest operations, about this many per instance (one tenant / all tenants together):

Operation Small Medium Large
decrypt-verify, 4 MB 2 / 5 4 / 9 6 / 13
decrypt-verify, 10 MB 1 / 2 1 / 3 2 / 5
convert-pkcs7-to-jose, 4 MB 1 / 3 2 / 5 2 / 5
convert-pkcs7-to-jose, 10 MB 1 / 1 1 / 2 1 / 2
analyze-pkcs7, 4 MB 4 / 9 7 / 14 7 / 14
analyze-pkcs7, 10 MB 2 / 4 3 / 6 3 / 6

The buffered service's heap grows with the profile (1152, 2048 and 3072 MiB); the streaming service's is 512 MiB on the small profile and 768 MiB on the medium and large ones. On the streaming service, the streams live at that moment come out of the budget first (about 4 MiB each), so the PKCS#7 figures fall as streams open: on the small profile, at its 32 streams, no 10 MB conversion is admitted. The large profile runs two instances of each service, which doubles every figure across the deployment.

A request beyond these is answered 503 with this problem type and Retry-After: 5, even when the instance still has room for other tenants: one tenant's share is half of the instance's budget, so no single tenant can take the capacity every other tenant depends on. Two tenants together reach the instance's budget (for example two concurrent 10 MB decrypt-and-verify requests, one from each). The numbers are per instance: each additional instance, and the larger heaps of the medium and large profiles, raise them. A smaller body than the limit counts for less, so more of them fit.

Common Causes

  • Several large buffered requests sent at the same time, for example a batch of concurrent PKCS#7 conversions or nested decrypt-and-verify calls near the payload limit.
  • One tenant running more large buffered requests at once than its share of an instance admits.
  • A request that declares no Content-Length: it is counted at the operation's full size limit, so it is refused sooner than a request that declares a smaller body.

Response Example

{
  "type": "https://docs.ankatech.co/errors/server-capacity-exceeded",
  "title": "Server Capacity Exceeded",
  "status": 503,
  "detail": "The service is holding as much of this kind of request as it can at the moment. Retry shortly.",
  "instance": "/api/v3/migration/convert-pkcs7-to-jose",
  "correlationId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "timestamp": 1730000000
}

Content-Type: application/problem+json — the response follows RFC 9457 Problem Details. The response carries Retry-After: 5. The body names no tenant, no current load and no limit.

How to Resolve

  1. Retry after the delay the Retry-After header names. Capacity frees as the requests in flight complete.
  2. Send large payloads one at a time, or fewer at once, rather than in parallel bursts.
  3. Declare the body's length (Content-Length) where your client can: a request is counted at the size it declares.
  4. For payloads that do not need to be buffered, use the streaming operations (/api/v3/crypto/stream/*, /api/v3/migration/stream/*), which are bounded separately.
  5. Operators: the budget is ankasecure.buffered.admission.heap-fraction (environment variable ANKASECURE_BUFFERED_ADMISSION_HEAP_FRACTION, default 0.70), the fraction of the instance's maximum heap these requests may hold together. A value outside 0.3–0.8 refuses startup. Adding instances or raising the heap raises the capacity; raising the fraction leaves less of the heap to the rest of the service.

For full schema definitions, examples, and interactive testing, see the Developer Hub Reference.