Troubleshooting: ATTACHMENT_STORAGE_UNAVAILABLE
Uploading a file in the Inbox attachment panel on a ticket fails with a503
and error.code equal to ATTACHMENT_STORAGE_UNAVAILABLE:
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
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.- Re-upload the same file after a short wait. A single retry inside the dashboard covers most transient incidents.
- 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.
- 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_idreturned on the failed response — it pins the exact upload attempt in the platform log, and - the checksum of the file bytes (compute
sha256of the file and send the hex). Support matches the checksum against the ingestion logs rather than re-deriving the file contents from your description.
- 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 on413 (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 returningATTACHMENT_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 — the sibling upload/download endpoints, and the scan-state fields that gate downloads of quarantined content.
- Troubleshooting: FILE_SCAN_QUARANTINED
— the intake-adjacent
402family 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: a402is the scanner saying hold, a503is the backend saying I can’t accept. - Error Code Reference — the envelope shape that
carries the
request_idyou escalate with.