The server deleted the entity while it was being edited locally.
The layer has no write target when the entry is pushed: its write block is absent,
switched off, or carries no endpoint.
⚠️ edition is not read again: an edit captured while the layer allowed it is still
sent once the permission has been withdrawn from the profile. What refuses it then is
the server.
The push was refused by the server for a reason replay cannot fix.
⚠️ NARROWED on 09/08/2026, and the previous width had a measured cost. This
member used to name EVERY non-409/non-404 failure, transient server outages included:
pushOne had a single branch for the whole spectrum. Since this reason is excluded from
REQUEUEABLE, a 503 during maintenance spent the replay budget and then left the capture
unreplayable — its only remaining exit being destruction. It now covers 4xx refusals
only, 404 and 501 excepted: malformed request, missing right, verb not allowed,
unprocessable entity. A server outage is retryBudgetExhausted; an unimplemented verb is
notImplementedByServer.
It also names a creation the server did not write: answered 409, or acknowledged with
an empty array, while the re-read through the client identity finds no row. The status
that travels with the entry is then the one of that answer — a 2xx in the second case.
The server does not implement the verb — HTTP 501.
🛑 ADDED on 09/08/2026, and the asymmetry it closes is the reason it exists. An
unimplemented verb on the CLIENT side (the rest dialect, push-engine.ts) already fell
through to retryBudgetExhausted and was therefore replayable; the same fact reported by
the SERVER landed on rejectedByServer and was not. Same fact, opposite outcomes.
It is set IMMEDIATELY — replaying a verb the server does not know only waits three times,
the same argument as deletedOnServer — and it IS replayable: the lifting condition is
the server being upgraded, which no local check can observe, so it is entrusted to the
operator exactly like retryBudgetExhausted.
The replay budget ran out — the entry failed MAX_REPLAY_ATTEMPTS times without the
server ever answering in a way replay could act on.
⚠️ ADDED on 05/08/2026, and it was missing for a precise reason. The three
motives above all name a CAUSE observed on the server or layer side. An exhausted
budget is not one: the server may never have answered. Reusing rejectedByServer
for a network timeout would have been a lie the code itself detects —
push-engine already distinguishes networkError from rejectedByServer.
✅ It also carries transient server failures since 09/08/2026 — 500, 502, 503, 504, 408, 429. Its sentence already covered them ("without the server ever answering in a way replay could act on"): upstream classification just was not sending them here.
The session is over — the server answered 401 on a write. A 403 says the identity is
known and lacks the right, which no sign-in changes: it stays rejectedByServer.
🛑 ADDED on 02/09/2026, and it closes the same asymmetry as
notImplementedByServer. A 401 landed on rejectedByServer, the one motive this
contract defines as "a reason replay cannot fix" — while an expired token is the
exact case replay DOES fix, as soon as the operator signs back in. A field capture
was therefore set aside with destruction as its only contractual exit, for a cause
that lifts by itself.
⚠️ It names a state of the SESSION, not a refusal by the server, and that is
why it is not a sub-case of rejectedByServer: in the connector's token mode the
401 the drain sees is even SYNTHETIC — the interceptor produces it after failing to
renew. Same status code, opposite meanings; the motive follows the meaning.
It is set IMMEDIATELY — replaying with a dead token only waits three times, the
argument of deletedOnServer and notImplementedByServer — and it IS replayable:
the lifting of the cause is a new sign-in, which nothing in the core can observe
(it knows nothing of the connector), so it is entrusted to the operator exactly
like retryBudgetExhausted.
The layer declares a write dialect the core does not speak — rest today.
🛑 A CLIENT-SIDE HOLE, AND IT USED TO BE SILENT. The drain refused the dialect by
name but returned an ordinary failure: the entry spent its replay budget against a
hole no replay can fill, then landed on retryBudgetExhausted — a motive that says
"the server never answered", when no request had ever been made. The operator could
requeue it, and it went round again.
It is set IMMEDIATELY, like layerNoLongerWritable, and its lifting is just as
OBSERVABLE: the layer's declaration changes, or it does not. That is why it is
requeueable and yet verified — see write/quarantine-api.ts.
Why an entry was set aside instead of replayed or dropped.