> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting: ATTACHMENT_STORAGE_UNAVAILABLE

> Recover an Inbox ticket attachment upload that returns 503 ATTACHMENT_STORAGE_UNAVAILABLE — tell a platform-side storage fault from a fixable validation problem, retry safely, and escalate with a request id and checksum.

# Troubleshooting: ATTACHMENT\_STORAGE\_UNAVAILABLE

Uploading a file in the Inbox attachment panel on a ticket fails with a `503`
and `error.code` equal to `ATTACHMENT_STORAGE_UNAVAILABLE`:

```json theme={null}
{
  "error": {
    "code": "ATTACHMENT_STORAGE_UNAVAILABLE",
    "message": "Attachment storage is currently unavailable. Please contact support if this continues.",
    "status": 503
  }
}
```

You see this on the proxy-upload call
`POST /api/v1/inbox/tickets/:id/attachments/:attId/content` — the upload
reached the API, passed validation, and then the object storage write failed.
The same code can surface on the registered-attachment flow when the signed
upload path hits the same backend fault, and on attachment downloads that read
from the same private bucket.

## Cause split: a storage fault, not a validation problem

`ATTACHMENT_STORAGE_UNAVAILABLE` is the classifier for "the storage backend
timed out or blew a 5xx." It fires **after** every intake gate has passed, so
the file itself is never the problem:

* File too large → `413 ATTACHMENT_TOO_LARGE`
* Empty body or malformed multipart → `422` / `415`
* Ticket or attachment id wrong, or outside your organization → `404` / `422`

Only when all of those clear and the write to the private attachment bucket
still fails does the route demote the fault to this `503`. Re-encoding the
file, changing its format, or trimming its name will never fix a `503` from
this code — the bytes never reached the decision point that rejects content.

The underlying truth is two-part: either the storage backend had a transient
incident (timeout, rolling restart, brief 5xx), or the deployment's bucket /
permission wiring drifted and the write fails the same way every time. The
retry path handles the first; the escalation path is for the second.

## Owner actions: retry with backoff, then escalate with evidence

The upload is **idempotent from your side** — the attachment row was not
committed, so re-uploading the same file cannot create duplicates or half-rows.

1. **Re-upload the same file** after a short wait. A single retry inside the
   dashboard covers most transient incidents.
2. If the failure repeats, use **exponential backoff** (roughly 1s, 2s, 4s,
   8s, capped at \~1 minute) instead of hammering the endpoint — a tight loop
   burns your rate-limit budget against a backend that is already failing.
3. If the failure **repeats with the same file at backoff**, treat it as a
   persistent storage fault and contact support with two things:
   * the `meta.request_id` returned on the failed response — it pins the exact
     upload attempt in the platform log, and
   * the **checksum of the file bytes** (compute `sha256` of the file and send
     the hex). Support matches the checksum against the ingestion logs rather
     than re-deriving the file contents from your description.

Keep the failed attempt's response in the ticket so the `request_id` and
checksum pair travels together; either alone forces support to guess.

## Tenant-owned first controls

Nothing about this fault is tenant-owned — it maps to platform storage
infrastructure, not your configuration. The only tenant-side control is the
upload hygiene that keeps you out of the validation codes above: stay inside
your organization's attachment size ceiling so a genuine oversize file lands
on `413` (a fixable client fault), not a `503` (a platform fault). If you see
`ATTACHMENT_STORAGE_UNAVAILABLE`, your configuration is exonerated by
definition and the fix is on the platform side.

## What NOT to do

* **Do not strip the message from the Inbox thread.** The failure is scoped
  to storage only — the conversation, the ticket, and the message text are
  intact. Delete-and-recreate removes context and gains nothing; the upload
  channel is the only thing at fault.
* **Do not edit the file and re-upload a renamed variant.** A storage 503 does
  not read the format; a changed name makes support triage harder by breaking
  the checksum you eventually send.
* **Do not delete-and-recreate the attachment row via the API.** There is no
  committed row for the failed attempt — the crash happens before commit — so
  a cleanup call has no target.
* **Do not disable attachment uploads at the tenant level.** A
  storage-unavailable fault is not a permission or policy gate; toggling a
  feature flag just hides a retryable error behind a different one.

## Escalate

If backoff-clean retries keep returning `ATTACHMENT_STORAGE_UNAVAILABLE` for
the same file, open a support ticket with the `meta.request_id` from the last
response plus the `sha256` of the bytes. Support reads the platform storage
log for that exact attempt, confirms whether the backend is mid-incident, and
either confirms the retry window or restores the write path. Do **not** keep
uploading new instances of the same attachment while you wait — you only
multiply failed rows' signal without changing the outcome.

## See also

* [Files API](/api-reference/endpoints/files) — the sibling upload/download
  endpoints, and the scan-state fields that gate downloads of quarantined
  content.
* [Troubleshooting: FILE\_SCAN\_QUARANTINED](/troubleshooting/file-scan-quarantined)
  — the intake-adjacent `402` family where the same upload surface instead
  holds a file inside the content-scanner's verdict path. The split is
  scan-state-first vs storage-first: a `402` is the scanner saying *hold*, a
  `503` is the backend saying *I can't accept*.
* [Error Code Reference](/reference/error-codes) — the envelope shape that
  carries the `request_id` you escalate with.
