Trilio Share Protection Backup and Recovery as a Service for OpenStack Manila shares
Guide

Error Codes

HTTP status codes returned by the service and their meanings


Overview

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.


Prerequisites

To make effective use of this reference you should already have:

  • A working Keystone token or trust that allows you to call abaca-api endpoints (see Authentication)
  • python-abacaclient installed if you are consuming the API through the Python SDK or the openstack share protection CLI
  • Familiarity with standard HTTP semantics (2xx, 4xx, 5xx ranges)
  • Access to your cloud's logging pipeline if you need to diagnose operator_action_required errors, because those failures are logged server-side with full tracebacks that are intentionally not surfaced to the caller

Installation
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.


Configuration

Error response behaviour is built into abaca-api and is not configurable by tenants. The following aspects are fixed by the service:

AspectFixed valueNotes
Error envelope format{"code", "title", "detail", "category"}All four fields are always present
category valuestenant_action_required or operator_action_requiredTells you who must act
Internal detail exposureNever500-class errors never include stack traces or internal state in the response body; those are written to the server log only
Content typeapplication/jsonAll 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"

Usage

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"
}
FieldTypeDescription
codestringStable, machine-readable identifier. Never localised. Use this in error-handling logic.
titlestringShort, human-readable summary of the error class.
detailstringInstance-specific explanation of what went wrong for this particular request.
categorystringEither 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 missing force=true flag 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 the code value and the timestamp of the failed request so the operator can correlate it with server logs.

HTTP status code summary

StatusMeaningTypical category
200Request succeeded
201Resource created successfully
202Asynchronous action accepted
204Resource deleted, no body
400Bad request — invalid input or missing fieldtenant_action_required
401Authentication required — missing or invalid Keystone tokentenant_action_required
403Forbidden — insufficient permissions, quota exceeded, or revoked trusttenant_action_required
404Resource not foundtenant_action_required
405Method not allowed for this URLtenant_action_required
409Conflict — duplicate resource, dependents exist, or repository lockedvaries (see table below)
422Unprocessable entity — bucket failed preflight checkstenant_action_required
500Internal server erroroperator_action_required
503Service unavailable — no workers or backend unreachableoperator_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:

codeHTTPcategoryWhen you see it
abaca.validation_error400tenant_action_requiredA request field is missing, has the wrong type, or fails a constraint. Check detail for the specific field.
abaca.unauthorized401tenant_action_requiredNo valid Keystone token was supplied, or the token has expired.
abaca.forbidden403tenant_action_requiredYour token is valid but your role does not allow this operation.
abaca.quota_exceeded403tenant_action_requiredYou have reached the maximum number of targets, policies, or backups allowed for your project.
abaca.trust_access_denied403tenant_action_requiredThe 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_found404tenant_action_requiredThe requested resource ID does not exist or does not belong to your project.
abaca.method_not_allowed405tenant_action_requiredYou used an HTTP method (e.g. DELETE) on a URL that does not support it.
abaca.conflict409tenant_action_requiredA resource with conflicting identity already exists, or the requested state change is not valid for the current state.
abaca.force_required409tenant_action_requiredYou 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_dependents409tenant_action_requiredYou tried to delete a backup target that still has protection policies or backups referencing it. Delete or reassign those resources first.
abaca.repository_locked409operator_action_requiredAnother 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_failed422tenant_action_requiredYour 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_unavailable503operator_action_requiredNo worker VM with a free capacity slot is currently registered. Contact your operator to scale up the worker fleet.
abaca.backend_unavailable503operator_action_requiredA required backend service (Manila, Barbican, or the message bus) could not be reached. This is a transient infrastructure issue.
abaca.invalid_state_transition500operator_action_requiredA 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_error500operator_action_requiredThe Kopia backup engine exited with a non-zero status during a job. Check operator logs for the Kopia stderr output.
abaca.network_strategy_precondition500operator_action_requiredThe 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_precondition500operator_action_requiredThe snapshot access strategy could not run because the selected strategy does not match this share's capabilities. Contact your operator.
abaca.internal_error500operator_action_requiredAn unexpected error occurred. Details are intentionally withheld from the response. Your operator can correlate the failure using server logs.

Examples

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

Troubleshooting

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.