Terminal Actor State
URI: https://docs.ankatech.co/errors/terminal-actor-state
HTTP Status: 422 Unprocessable Entity
When you see this
The Cryptographic Actor the request acts on is DISABLED. DISABLED is terminal and irreversible, so the actor can never act again. Two families of request answer it:
- You asked an Internal Crypto Use Case* to suspend or resume, and the actor that aggregate owns is
DISABLED— which makes the aggregate both un-suspendable and un-resumable. - You asked for new identity material for a
DISABLEDactor: minting or rotating a credential (POST …/actors/{actorId}/credentials,POST …/actors/{actorId}/credentials/rotate), or binding an external issuer's subject to it (POST …/workload-identity/actors/{actorId}/issuer-bindings, on the tenant plane or the platform-on-behalf route). A credential or binding for an actor that can never authenticate would be material with nothing behind it, so it is refused before anything is written — no row, no secret, no audit event.
This is a distinct 422 from Invalid State Transition, and the distinction is the point: that one means the current state does not permit this verb, and a different verb — or the same verb later — succeeds. This one means no verb on this aggregate will ever succeed again. A client must be able to tell those apart on the type member rather than by string-matching detail, which is prose and may be reworded.
The refusal is raised inside the request's own transaction, before anything is written, so nothing was changed: the aggregate keeps the status it had, and no credential or binding exists.
Common Causes
- The owning tenant was closed, and the closure cascade terminal-disabled the actor.
- The owning Application was deleted, and its delete cascade terminal-disabled the actor.
Neither is a mistake on the use case's part — the actor reaches DISABLED independently of the aggregate, which is why this state is reported as a recognisable condition rather than as a generic invalid transition.
Response Example
{
"type": "https://docs.ankatech.co/errors/terminal-actor-state",
"title": "Terminal Actor State",
"status": 422,
"detail": "Use case cannot be suspended: its Cryptographic Actor is DISABLED, which is terminal and irreversible, so the workload can never act again. The actor reaches that state independently of this use case — a tenant closure, or the owning Application's delete cascade. Exit: revoke this use case (DELETE) to bring the aggregate to a consistent terminal state, then provision a new one if the workload is still required.",
"instance": "/api/v3/admin/tenants/00000000-0000-0000-0000-000000000001/internal-crypto-use-cases/9f1c4d2e-7b83-4a51-95e6-2c0d81ab6f34/suspend",
"timestamp": 1730000000,
"extensions": {
"requestId": "550e8400-e29b-41d4-a716-446655440000"
}
}
Content-Type: application/problem+json — the response follows RFC 9457 Problem Details.
How to Resolve
- Do not retry. The actor's state is irreversible, so every retry answers this same refusal. Revoking the actor's existing credentials and removing its existing bindings remain available.
- Revoke the use case —
DELETE /api/v3/admin/tenants/{tenantId}/internal-crypto-use-cases/{useCaseId}?reason=...— to bring the aggregate to a consistent terminal state. Revoke tolerates aDISABLEDactor because disabled is the state it is driving towards. - If the workload is still required, provision a replacement use case. The referenced keys are untouched by any of this, so the new use case can name the same
kid.
For full schema definitions, examples, and interactive testing, see the Developer Hub Reference.