Error codes
Every error frame carries {1: code, 2: wire_name, 3: message, 4: detail}. Clients switch on code for machine-handling; log message for humans; read detail for context-specific data.
Core SCWP errors (SCWP §8)#
| Code | Wire name | Meaning | detail shape |
|---|---|---|---|
1 |
E_MALFORMED |
Frame or body couldn't be parsed | {} |
2 |
E_UNAUTHORIZED |
Missing / invalid auth | {} |
3 |
E_NOT_FOUND |
Channel / envelope / device / wrap missing | {} |
4 |
E_FORBIDDEN |
Caller not permitted (member check, tenant policy, etc.) | Varies |
5 |
E_ALREADY_CLAIMED |
One-shot clip has been claimed | {claimed_by: text, at: u64} |
6 |
E_GONE |
Envelope tombstoned (expired / claimed / deleted / revoked) | {} |
7 |
E_RATE_LIMITED |
Rate limit exceeded | {retry_after_ms: u64} |
8 |
E_TOO_LARGE |
Frame or ciphertext over ceiling | {limit: u64, actual: u64} |
9 |
E_KEY_STALE |
key_version older than the channel's current |
{current: u32} |
10 |
E_EXISTS |
Team channel name collision | {} |
11 |
E_WRAPS_INCOMPLETE |
ONE_SHOT publish missing wraps for member devices |
{missing: [device_id]} |
12 |
E_BAD_SIG |
Envelope signature failed verification | {} |
13 |
E_NOT_ONESHOT |
clip.claim on a non-ONE_SHOT clip |
{} |
14 |
E_QUOTA |
Legacy quota exceeded (deprecated) | {} |
SCSG / scratch / media errors (v0.1.3+)#
| Code | Wire name | Meaning | detail shape |
|---|---|---|---|
15 |
E_QUOTA_EXCEEDED |
Scratch quota or SCSG concurrent-session cap | {scope, used, limit} |
16 |
E_UNKNOWN_SERVICE |
session.open with unregistered service_id |
{service_id: text} |
17 |
E_NO_ONLINE_DEVICE |
SCSG open can't find an online T1 device for the user | {user_id: text} |
18 |
E_OUT_OF_SCOPE |
SCSG operation outside grant's channel_ids or missing a required capability |
{granted, requested} |
19 |
E_GRANT_EXPIRED |
SCSG grant is closed / denied / expired / revoked / past TTL | {status: text} |
Media proxy errors (planned)#
| Code | Wire name | Meaning |
|---|---|---|
| — | E_ORIGIN_OFFLINE |
media.fetch for ref_origin with origin device offline (returned as generic error today; distinct code lands in v0.1.4) |
| — | E_TRANSFER_TIMEOUT |
ref_origin transfer exceeded scaiclip_mediaproxy_transfer_timeout_s |
| — | E_HASH_MISMATCH |
Scratch commit sha256 doesn't match reservation |
| — | E_TOO_MANY_TRANSFERS |
Per-channel concurrent-transfer cap hit |
HTTP errors#
Public-download route (GET /p/{token}) uses standard HTTP codes:
| Status | Meaning |
|---|---|
200 |
Landing page returned (password-bound link) |
302 |
Redirect to presigned S3 URL |
404 |
Non-existent token (constant-time; indistinguishable from 410) |
410 |
Token revoked / expired / download-limit reached / underlying object gone |
429 |
Per-IP or per-token rate limit exceeded |
REST endpoints on /v1/modules/scaiclip/… map service errors to standard 400/403/404 with the wire-name / message in the JSON envelope.
Retry semantics#
Errors safe to retry as-is:
E_RATE_LIMITED— waitretry_after_ms, retry.E_NOT_FOUNDonkey.wraps.get— wait for awraps.readypush, retry.E_TRANSFER_TIMEOUT— restartmedia.fetch.
Errors that require caller action first:
E_KEY_STALE— re-runchannel.list+key.wraps.get.E_WRAPS_INCOMPLETE— callchannel.devices, generate wraps for missing devices, retryclip.publish.E_ORIGIN_OFFLINE— surface to user ("sender is offline").E_QUOTA_EXCEEDED— surface with theoldest_reclaimablehint fromdetail.
Errors that should not be retried without a fix:
E_MALFORMED,E_BAD_SIG,E_UNAUTHORIZED,E_FORBIDDEN— these are protocol / auth bugs on the caller side.
Audit correlation#
Every rejection emits an audit event with the same resource_type / resource_id as the successful path. Filter for status = 'DENIED' in /v1/audit queries to find where callers hit these errors.
Related#
- SCWP wire reference
- SCSG verb reference
- Troubleshooting — common causes and fixes for each error.