Policy Lock Timeout
URI: https://docs.ankatech.co/errors/policy-lock-timeout
HTTP Status: 503 Service Unavailable
When you see this
A policy write needed the active deployment policy row and could not take its lock within the bounded wait, because a concurrent deployment policy write was still holding it. Nothing was changed.
Every tenant policy write is validated against the deployment policy as a ceiling, so it reads the deployment row under a shared lock and waits for an in-flight deployment write to commit before comparing against the committed row. A deployment policy write takes the row exclusively. Both waits are bounded by a statement timeout of five seconds; when it expires, the write has done nothing and the request is answered with this status.
503 and not 500: nothing is wrong with the request and nothing is defective — the same body a moment later succeeds. A 500 would send an operator looking for a defect that does not exist.
Common Causes
- A deployment policy
PUT, reset, or bulkPUTwas in flight and held the deployment policy row for longer than the five-second wait — for example, an algorithm policy change whose cross-tenant cascade check had many tenants to compare. - Two deployment policy writes arriving close together; the second waits for the first.
- A tenant policy write, reset, override-flag toggle, hydration, or tenant provisioning that landed while a deployment write was committing.
- An unusually slow deployment write holding the row — for example one contending on a busy database.
Response Example
{
"type": "https://docs.ankatech.co/errors/policy-lock-timeout",
"title": "Policy Lock Timeout",
"status": 503,
"detail": "The deployment policy is being changed by another operation and the lock could not be acquired within the allowed time. Nothing was changed; retry the operation shortly.",
"instance": "/api/v3/admin/tenants/3f9a1b62-5d47-4c8e-b0a2-91d7e4f60cc5/policies/algorithm-availability",
"timestamp": 1730000000,
"extensions": {
"requestId": "550e8400-e29b-41d4-a716-446655440000"
}
}
Content-Type: application/problem+json — the response follows RFC 9457 Problem Details.
The detail is a fixed string: it names no row identifier, no SQL, and no lock holder. The underlying cause is retained for server-side logging only.
How to Resolve
- Wait the number of seconds in the
Retry-Afterheader (5) and retry the same request. Nothing was written, so the retry is safe and is the intended response. - If an automation issues deployment and tenant policy writes concurrently, serialise them client-side: apply the deployment policy first, then the tenant policies.
- If it persists, investigate what is holding the deployment policy row open for longer than the wait bound — persistent occurrences point at a slow deployment write, not at contention.
Operations that emit it
Every handler that reads the deployment policy row under the bounded lock declares this response:
| Method | Endpoint | Lock taken |
|---|---|---|
| PUT | /api/v3/admin/platform/deployment/policies | exclusive (algorithm write, then rotation write) |
| PUT | /api/v3/admin/platform/deployment/policies/algorithm-availability | exclusive |
| PUT | /api/v3/admin/platform/deployment/policies/rotation | exclusive |
| POST | /api/v3/admin/platform/deployment/policies/algorithm-availability/reset | exclusive; the active row is updated in place, never deactivated and re-inserted |
| POST | /api/v3/admin/platform/deployment/policies/rotation/reset | exclusive; the active row is updated in place, never deactivated and re-inserted |
| POST | /api/v3/admin/platform/deployment/policies/algorithm-availability/hydrate-all-tenants | shared, once per tenant row created |
| POST | /api/v3/admin/platform/tenants/provision | shared, for the initial policy set |
| PUT | /api/v3/admin/tenants/{tenantId}/policies/algorithm-availability | shared |
| DELETE | /api/v3/admin/tenants/{tenantId}/policies/algorithm-availability | shared |
| PUT | /api/v3/admin/tenants/{tenantId}/policies/rotation | shared |
| DELETE | /api/v3/admin/tenants/{tenantId}/policies/rotation | shared |
| PATCH | /api/v3/admin/tenants/{tenantId}/policies/override-flag | shared, only when a missing row is created |
The deployment lifecycle policy has its own write path and does not take this lock.
For full schema definitions, examples, and interactive testing, see the Developer Hub Reference.