Error Codes
HTTP status codes returned by the service and their meanings
This page documents every HTTP status code that the abaca-api service can return and explains the structure of error responses. Understanding these codes helps you distinguish between failures you can fix yourself — such as a misconfigured bucket or a missing field — and failures that require your cloud operator to intervene. Every error carries a machine-readable code field and a category field that tell you precisely who must act and why, making it straightforward to handle errors programmatically or route them to the right team.
To make effective use of this reference you should already have:
- A working Keystone token or trust that allows you to call
abaca-apiendpoints (see Authentication) python-abacaclientinstalled if you are consuming the API through the Python SDK or theopenstack share protectionCLI- Familiarity with standard HTTP semantics (2xx, 4xx, 5xx ranges)
- Access to your cloud's logging pipeline if you need to diagnose
operator_action_requirederrors, because those failures are logged server-side with full tracebacks that are intentionally not surfaced to the caller
pip install python-abacaclient
All error-handling behaviour described here is built into the service and the client library — no additional configuration is required to receive structured error responses.
Error response behaviour is built into abaca-api and is not configurable by tenants. The following aspects are fixed by the service:
| Aspect | Fixed value | Notes |
|---|---|---|
| Error envelope format | {"code", "title", "detail", "category"} | All four fields are always present |
category values | tenant_action_required or operator_action_required | Tells you who must act |
| Internal detail exposure | Never | 500-class errors never include stack traces or internal state in the response body; those are written to the server log only |
| Content type | application/json | All error responses, including 404 and 405, use the canonical JSON envelope |
If you are using python-abacaclient, every non-2xx response is raised as an AbacaClientError. You can access the envelope fields as attributes:
from abacaclient.exceptions import AbacaClientError
try:
client.backups.create(...)
except AbacaClientError as exc:
print(exc.status) # integer HTTP status
print(exc.code) # e.g. "abaca.validation_error"
print(exc.title) # short human-readable summary
print(exc.detail) # instance-specific message
print(exc.category) # "tenant_action_required" or "operator_action_required"
Reading the error envelope
Every error response from abaca-api — regardless of status code — uses the same four-field JSON envelope:
{
"code": "abaca.target_preflight_failed",
"title": "Backup Target Preflight Failed",
"detail": "Bucket does not have object lock enabled.",
"category": "tenant_action_required"
}
| Field | Type | Description |
|---|---|---|
code | string | Stable, machine-readable identifier. Never localised. Use this in error-handling logic. |
title | string | Short, human-readable summary of the error class. |
detail | string | Instance-specific explanation of what went wrong for this particular request. |
category | string | Either tenant_action_required or operator_action_required. See below. |
Using category to route failures
The category field is the fastest way to decide what to do next:
tenant_action_required— You can fix this without involving your cloud operator. Common causes include invalid request fields, a misconfigured S3 bucket, a revoked Keystone trust or Barbican secret, a quota limit, or a missingforce=trueflag on a destructive operation.operator_action_required— The infrastructure needs attention. No worker VMs are available, a backend service is down, or an internal consistency error occurred. File a support ticket and include thecodevalue and the timestamp of the failed request so the operator can correlate it with server logs.
HTTP status code summary
| Status | Meaning | Typical category |
|---|---|---|
| 200 | Request succeeded | — |
| 201 | Resource created successfully | — |
| 202 | Asynchronous action accepted | — |
| 204 | Resource deleted, no body | — |
| 400 | Bad request — invalid input or missing field | tenant_action_required |
| 401 | Authentication required — missing or invalid Keystone token | tenant_action_required |
| 403 | Forbidden — insufficient permissions, quota exceeded, or revoked trust | tenant_action_required |
| 404 | Resource not found | tenant_action_required |
| 405 | Method not allowed for this URL | tenant_action_required |
| 409 | Conflict — duplicate resource, dependents exist, or repository locked | varies (see table below) |
| 422 | Unprocessable entity — bucket failed preflight checks | tenant_action_required |
| 500 | Internal server error | operator_action_required |
| 503 | Service unavailable — no workers or backend unreachable | operator_action_required |
Complete error code reference
The following table lists every stable error code value, the HTTP status it maps to, which category it carries, and what it means:
code | HTTP | category | When you see it |
|---|---|---|---|
abaca.validation_error | 400 | tenant_action_required | A request field is missing, has the wrong type, or fails a constraint. Check detail for the specific field. |
abaca.unauthorized | 401 | tenant_action_required | No valid Keystone token was supplied, or the token has expired. |
abaca.forbidden | 403 | tenant_action_required | Your token is valid but your role does not allow this operation. |
abaca.quota_exceeded | 403 | tenant_action_required | You have reached the maximum number of targets, policies, or backups allowed for your project. |
abaca.trust_access_denied | 403 | tenant_action_required | The Keystone trust or Barbican secret used by Abacá for this target has been revoked or is no longer accessible. Re-create the trust or rotate the secret and re-enroll the target. |
abaca.not_found | 404 | tenant_action_required | The requested resource ID does not exist or does not belong to your project. |
abaca.method_not_allowed | 405 | tenant_action_required | You used an HTTP method (e.g. DELETE) on a URL that does not support it. |
abaca.conflict | 409 | tenant_action_required | A resource with conflicting identity already exists, or the requested state change is not valid for the current state. |
abaca.force_required | 409 | tenant_action_required | You requested an in-place restore without setting force=true. Because in-place restore overwrites the share's contents, the API requires an explicit opt-in. |
abaca.target_has_dependents | 409 | tenant_action_required | You tried to delete a backup target that still has protection policies or backups referencing it. Delete or reassign those resources first. |
abaca.repository_locked | 409 | operator_action_required | Another job currently holds the single-writer lock on this Kopia repository. This resolves automatically when the running job finishes. If the lock is stuck, an operator can trigger reconciliation via POST /admin/reconciliation/trigger. |
abaca.target_preflight_failed | 422 | tenant_action_required | Your S3 bucket did not pass the conformance preflight checks. Common causes: object lock is not enabled, the bucket uses path-style addressing when virtual-hosted is required, expiration lifecycle rules are present, or Abacá cannot reach the endpoint. Check detail for the specific check that failed. |
abaca.worker_unavailable | 503 | operator_action_required | No worker VM with a free capacity slot is currently registered. Contact your operator to scale up the worker fleet. |
abaca.backend_unavailable | 503 | operator_action_required | A required backend service (Manila, Barbican, or the message bus) could not be reached. This is a transient infrastructure issue. |
abaca.invalid_state_transition | 500 | operator_action_required | A job was asked to move between two states with no legal edge in the state machine. This is an internal consistency error; contact your operator. |
abaca.kopia_error | 500 | operator_action_required | The Kopia backup engine exited with a non-zero status during a job. Check operator logs for the Kopia stderr output. |
abaca.network_strategy_precondition | 500 | operator_action_required | The network attachment strategy could not run because the share's Manila metadata is incomplete. This is a conductor-side bug; contact your operator. |
abaca.snapshot_strategy_precondition | 500 | operator_action_required | The snapshot access strategy could not run because the selected strategy does not match this share's capabilities. Contact your operator. |
abaca.internal_error | 500 | operator_action_required | An unexpected error occurred. Details are intentionally withheld from the response. Your operator can correlate the failure using server logs. |
Example 1 — Successful backup creation (202 Accepted)
A backup is accepted asynchronously. Poll the returned job to track progress.
curl -s -X POST https://abaca.example.com/backups \
-H "X-Auth-Token: $OS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"share_id": "a1b2c3d4-...", "target_id": "e5f6a7b8-..."}'
Expected response — HTTP 202:
{
"id": "9c0d1e2f-...",
"share_id": "a1b2c3d4-...",
"target_id": "e5f6a7b8-...",
"status": "queued",
"crash_consistent": true,
"kopia_snapshot_id": null
}
Example 2 — Validation error (400)
Omitting a required field returns a 400 with abaca.validation_error.
curl -s -X POST https://abaca.example.com/backups \
-H "X-Auth-Token: $OS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"share_id": "a1b2c3d4-..."}'
Expected response — HTTP 400:
{
"code": "abaca.validation_error",
"title": "Invalid Request",
"detail": "'target_id' is a required field.",
"category": "tenant_action_required"
}
Example 3 — Resource not found (404)
Referencing an ID that does not exist or does not belong to your project returns 404.
curl -s https://abaca.example.com/backups/does-not-exist \
-H "X-Auth-Token: $OS_TOKEN"
Expected response — HTTP 404:
{
"code": "abaca.not_found",
"title": "Resource Not Found",
"detail": "The requested URL was not found.",
"category": "tenant_action_required"
}
Example 4 — Bucket preflight failure (422)
Registering a target whose bucket lacks object lock returns 422.
curl -s -X POST https://abaca.example.com/targets \
-H "X-Auth-Token: $OS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "name": "my-target", "endpoint": "https://s3.example.com", "bucket": "my-bucket", "barbican_secret_refs": ["https://barbican.example.com/v1/secrets/..."] }'
Expected response — HTTP 422:
{
"code": "abaca.target_preflight_failed",
"title": "Backup Target Preflight Failed",
"detail": "Bucket 'my-bucket' does not have S3 object lock enabled. Object lock must be configured at bucket creation time.",
"category": "tenant_action_required"
}
Fix: Create a new bucket with object lock enabled at creation time (this cannot be added retroactively), then register that bucket as your target.
Example 5 — In-place restore rejected without force (409)
Attempting to restore over the original share without the required opt-in:
curl -s -X POST https://abaca.example.com/restores \
-H "X-Auth-Token: $OS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"backup_id": "9c0d1e2f-...", "mode": "in_place"}'
Expected response — HTTP 409:
{
"code": "abaca.force_required",
"title": "Destructive Operation Requires force=true",
"detail": "In-place restore will overwrite the share's contents. Set force=true to confirm.",
"category": "tenant_action_required"
}
Fix: Repeat the request with "force": true in the body.
Example 6 — No worker available (503)
{
"code": "abaca.worker_unavailable",
"title": "No Worker Available",
"detail": "No worker VM with a free slot is currently registered.",
"category": "operator_action_required"
}
This is not something you can fix as a tenant. Contact your operator to scale the worker fleet or wait for a running job to complete and free a capacity slot.
Example 7 — Handling errors in Python with python-abacaclient
from abacaclient.exceptions import AbacaClientError
try:
backup = client.backups.create(
share_id="a1b2c3d4-...",
target_id="e5f6a7b8-..."
)
except AbacaClientError as exc:
if exc.category == "tenant_action_required":
# Something in your request or configuration needs fixing
print(f"Fix required: [{exc.code}] {exc.detail}")
elif exc.category == "operator_action_required":
# Infrastructure issue — escalate
print(f"Operator alert: [{exc.code}] {exc.title} (HTTP {exc.status})")
else:
raise
Use the code and category fields in every error response as your first diagnostic signal — they tell you exactly what failed and who needs to act.
Symptom: Every request returns HTTP 401 with abaca.unauthorized.
Likely cause: Your Keystone token is missing, malformed, or has expired. Tokens have a finite lifetime set by your Keystone configuration.
Fix: Re-authenticate with Keystone to obtain a fresh token and set it in the X-Auth-Token header. If you are using python-abacaclient or the openstack share protection CLI, re-source your openrc file or refresh your clouds.yaml credentials.
Symptom: HTTP 403 with abaca.trust_access_denied.
Likely cause: The Keystone trust or Barbican secret that Abacá uses to act on your behalf for this backup target has been revoked, rotated, or expired since the target was enrolled.
Fix: Identify which target is affected (the detail field will name it), delete and re-enroll the target using valid Barbican secret references, and verify that the new trust is created successfully during enrollment.
Symptom: HTTP 422 with abaca.target_preflight_failed.
Likely cause: Your S3 bucket failed one or more of the conformance preflight checks. The most common causes are: object lock was not enabled at bucket creation time; expiration lifecycle rules are present on the bucket; the bucket is not reachable from Abacá's network; or the credentials stored in Barbican do not have read/write access.
Fix: Read the detail field to identify the specific failing check. If object lock is missing, you must create a new bucket — it cannot be added retroactively. Remove any expiration lifecycle rules. Supported S3-compatible backends are AWS S3, MinIO, Ceph RGW, ODF, and Wasabi.
Symptom: HTTP 409 with abaca.target_has_dependents when deleting a target.
Likely cause: One or more protection policies or backups still reference the target you are trying to delete. Abacá refuses to delete the target to avoid orphaning your backup data.
Fix: List and delete all policies attached to this target, then list and delete (or reassign) all backups stored in it. Once no policies or backups reference the target, the delete request will succeed.
Symptom: HTTP 409 with abaca.force_required during a restore.
Likely cause: You requested an in-place restore ("mode": "in_place") without explicitly confirming the destructive operation.
Fix: Add "force": true to your restore request body. Be aware that in-place restore overwrites the current contents of the original share.
Symptom: HTTP 409 with abaca.repository_locked.
Likely cause: Another job is currently writing to the same Kopia repository and holds the single-writer lock. This is expected during normal concurrent operation.
Fix: Wait for the running job to finish — the lock is released automatically when the job transitions out of the transferring or finalizing state. If the lock appears stuck (no job is actively running), ask your operator to trigger reconciliation via POST /admin/reconciliation/trigger.
Symptom: HTTP 503 with abaca.worker_unavailable.
Likely cause: All registered worker VMs are fully occupied (running jobs equal their capacity slot count), or no workers are registered at all.
Fix: This is operator_action_required. Contact your operator to scale up the worker fleet or wait for a running job to finish and free a slot. You can check fleet status at GET /admin/workers if you have admin access.
Symptom: HTTP 503 with abaca.backend_unavailable.
Likely cause: A required backend service — Manila, Barbican, or the RabbitMQ message bus — is temporarily unreachable from the Abacá control plane.
Fix: This is operator_action_required. Retry the request after a short delay. If the error persists, contact your operator and provide the code value and the timestamp so they can correlate it with service logs.
Symptom: HTTP 500 with abaca.internal_error (or any other operator_action_required 500-class code).
Likely cause: An unexpected internal error occurred. The response body intentionally contains no internal details to avoid leaking sensitive information.
Fix: Record the exact timestamp and any resource IDs from the failed request. Contact your operator — the full traceback is written to the abaca-api server log and can be retrieved by correlating the timestamp. If you received abaca.kopia_error, the operator should also inspect the Kopia stderr output captured by the worker agent for that job.