Array Sync Rejects

A client pushes array ops to Origin over the sync WebSocket. Origin answers each op it does not apply with a typed reason. Each reason has one fixed class. The class tells the client what to do next. A client never parses the detail text.

Frames

FrameOpcodeDirectionOrigin's answer
ArrayDelta0x90client → serverNothing when the op applies. One ArrayReject when it does not.
ArrayDeltaBatch0x91client → serverAlways one ArrayOpResults, with one result per op.
ArraySchema0x94client → serverNothing when the schema commits. One ArrayReject when it does not.
ArrayAck0x95client → serverNothing when recorded. One ArrayAdvisoryRefused when refused.
ArrayCatchupRequest0x97client → serverSnapshot or op frames when served. One ArrayAdvisoryRefused when refused.
ArrayReject0x96server → clientNames the op by HLC, plus the reason and a detail string.
ArrayOpResults0x98server → clientOne result per op, in the order the ops arrived.
ArrayAdvisoryRefused0x99server → clientNames the array, the refused request (Ack or CatchupRequest), the reason, and a detail string.

An array frame whose body does not decode draws an ArrayReject with reason ShapeInvalid, an empty array name, and an all-zero HLC. That reject names no single op. The client holds every op it has in flight.

Advisory Refusals

An ArrayAck or ArrayCatchupRequest names no local op. Origin refuses one with ArrayAdvisoryRefused, never with ArrayReject, so a refusal never touches the client's push window. Its reason and class follow the table below. A catch-up for an array Origin does not hold draws ArrayUnknown. A catch-up that fails while Origin serves it draws the reason of the typed error.

Batches

Each op in an ArrayDeltaBatch applies on its own. A rejected op never stops the ops after it. Array ops are independent CRDT writes ordered by HLC, so no op in a batch depends on an earlier one.

ArrayOpResults.results holds one entry per entry of op_payloads, in the same order. Each entry carries the op's HLC and one outcome:

OutcomeMeaning
AppliedThe op committed and applied.
DuplicateOrigin already held the op's HLC. Nothing changed.
Rejected { reason, detail }The op did not apply. The reason's class names the next action.

An op whose payload does not decode carries an all-zero HLC. Its position in results still names it.

An inbound array snapshot answers the same way. When the last chunk arrives, Origin attempts every op in the snapshot and sends one ArrayOpResults.

Reasons and Classes

ReasonClassCause
EngineUnavailableTransientThe cluster gave the op no verdict: dispatch capacity refusal (SERVER_OVERLOAD, SQLSTATE 57P03), leader change, lost quorum, deadline, rate gate, or an unknown commit outcome.
SnapshotCapacityTransientThe session's inbound snapshot buffers are full.
SnapshotBufferLostTransientA fault on Origin discarded the session's partial inbound snapshots. Re-send the whole snapshot, header first.
SchemaTooNewSchemaRequiredThe op's schema HLC is newer than Origin's copy of the schema.
ArrayUnknownSchemaRequiredOrigin holds no schema for the array.
UnauthenticatedReauthenticateThe frame arrived before the session authenticated.
RetentionFloorCatchUpThis replica's catch-up cursor is below Origin's op-log GC floor.
ShapeInvalidPermanentThe op failed decoding or shape validation.
EngineRefusedPermanentThe engine refused the op on its merits: constraint, type, producer epoch fence, or an encoding error.
PermissionDeniedPermanentThe session identity lacks the permission the op needs.

Origin derives the reason from the typed engine error. It shares that classification with every other sync engine ack. A failure that applied nothing and can land later is EngineUnavailable.

Client Actions

ClassAction
TransientKeep the op queued. Re-send it unchanged after a short backoff, in the same session.
SchemaRequiredSend ArraySchema for the array, then re-send the op.
ReauthenticateComplete the handshake or token refresh, then re-send.
CatchUpSend ArrayCatchupRequest. The pending queue stays unchanged.
PermanentRemove the op from the queue and dead-letter it.

Re-sending an op that did commit is safe. Origin dedups array ops by HLC, so the second apply reports Duplicate and changes nothing.