Appearance
Errors
Error responses are JSON:
json
{
"kind": "queue_not_found",
"message": "queue orders does not exist",
"details": {"queue": "orders"}
}kind is present where the status alone is ambiguous; details carries the facts the message states, as fields fixed per kind, and is absent for kinds that have none. Match on the status, kind and details; the message text is for humans and is not part of the contract. A fact never lives only in message: what a client can act on is a field, in the body or in a header. A fact HTTP has a header for is that header and is not repeated in the body: the wait on a 429 is Retry-After, whole seconds.
Statuses without a kind mean what HTTP says: 401 bad credentials, 402 the account is suspended, 403 the key is blocked, 411 a send without Content-Length, 503 the spool is momentarily unavailable — retry.
Kinds
Where a kind carries details, its shape follows the meaning — the shape, not a sample: values are the wire types.
| Kind | Status | Retry | Meaning |
|---|---|---|---|
invalid_token | 400 | Never. | The token — lease or handle — is malformed: a bug or tampering. |
dedup_disabled | 400 | No — set dedupWindowSeconds or drop the key. | The send carries a dedup key but the queue has no dedup window. |
unknown_header | 400 | No — fix the request. | A Spooler-* request header the operation does not define — likely meant for another endpoint. jsonc |
retention_limit | 403 | No. | The requested retention exceeds the plan's limit. jsonc |
queue_limit | 403 | No — delete unused queues. | The plan's queue limit is reached. jsonc |
queue_not_found | 404 | No. | The queue does not exist. A verdict, not lag. jsonc |
spool_not_found | 404 | No. | No spool of that name on the account. A verdict, like queue_not_found; a spool just created is visible within seconds. |
queue_exists | 409 | No. | A create names a queue that already exists. |
queue_deleting | 409 | No. | The queue refuses the operation while its deletion completes. |
queue_busy | 409 | Yes. | A concurrent settings change lost a race. |
not_failed | 409 | No — a verdict; the view was fresh. | Recover was given a current handle to a message that is not failed. |
dedup_in_flight | 409 | Yes, after it settles. | A send with the same dedup key has not settled yet. |
dedup_claimed | 409 | No — ack explicitly if folding into it was the intent. | An ack-and-send's dedup key is held by another message; nothing was acked. jsonc |
stale_token | 410 | A lease: receive again. A handle: list again. | The token verified but no longer names anything: the lease expired, was settled, or predates a primary change; the message a handle named moved on. |
payload_too_large | 413 | No. | The message exceeds the size limit (see limits). |
rate_limited | 429 | After the Retry-After seconds. | The per-account request rate cap is exceeded (see request rate). |
operation_unconfirmed | 500 | A send: only on a queue with a dedup window, carrying the same key (see deduplication). | The write was accepted but its durability is unknown. |
spool_full | 507 | After making room. | The spool holds its plan's maximum stored messages or payload bytes (see limits). jsonc |
Retrying a send
A send is the one operation a retry can duplicate, so its statuses fall in two classes:
- Appended nothing. Every
4xx,503and507: each is answered before the append is attempted, so retrying one can never duplicate the message, with or without a dedup key. Most are verdicts; the transient ones are409dedup_in_flight,429and503. Such a status speaks for this attempt only: it says nothing about an earlier attempt whose response was lost. - May have appended. A
500of any kind, a502or504, or no response at all.operation_unconfirmedsays so explicitly — the entry was written but its durability is unknown — and any other500is not proven to precede the write. A502or504means the gateway lost the node serving the spool and cannot tell one that was unreachable from one that went away after committing; a dropped connection or a client timeout is the same from the client's side. Retry these only on a queue with a dedup window, carrying the key the first attempt carried, while the window still remembers it — see deduplication. Without a key, a retry can append twice.
Ack-and-send needs no key to retry in either class: a retry of a committed call fails with 410 and appends nothing.
Settles are safe to retry in the same sense: repeating a committed settle answers 410 and changes nothing. A 410 on the retry does not prove the first attempt succeeded, though — the lease may instead have expired or become stale — so which statuses are worth retrying is the table above, for every operation alike; a 502 or 504 on a settle is the gateway losing the node, and the retry cannot tell the two apart.
A receive changes delivery state, so a retry after a lost response is a second receive: it may lease a different message, while the first delivery stays leased until its lease times out and it is redelivered — or, on a queue with no lease timeout, until a primary change drops it.