Skip to content

Error Handling

Every user-state write is queued and drained in order. When the server responds, the SDK classifies the result into one of the tags below, which decides whether the write is retried, dropped, or committed, and what happens to the drain queue.

Write-result contract

Failure kindAPI status or codeSDK tagDrain behaviorRetried?
Content rejection400 patch_invalid, patch_unsupported_operation, patch_invalid_path, patch_missing_value, patch_path_untraversable, patch_invalid_array_index, patch_invalid_state_root, patch_state_too_large; 415rejectedDropped via shiftAndRebuild()No
Transient failure5xx, no_response/network error, 429 rate_limited, 404 configuration, any other unrecognized status or unparseable 400 bodyretryKept queuedYes, standard backoff
Quota exceeded402quotaKept queuedYes, slower quota pace: jittered ~2.5-5 min base, doubling to a 30-minute cap. Never halted
Authorization rejection ('real' verdict)401/403authzSession reset: queue and optimistic state are cleared, and the stored credential is clearedNo
Authorization rejection ('stale' verdict)401/403authzKept queuedYes, standard backoff
Version conflict412conflictTransparent refetch-and-replay, no error publishedNot applicable
Committed but unverifiedMalformed 2xx envelope after a PATCHcommitted-unverifiedTreated as committed: head removed, version cleared to force a 412 reconcile on the next write. Surfaced once as a protocol errorNever resent, never rolled back
Confirmed no-oppatch_path_not_foundnoopShifted silentlyNo error

The 'real' vs 'stale' distinction for a 401/403 depends on whether the credential that was rejected is still the client's current credential at the time the rejection is processed. A 'real' verdict means the client's session is genuinely no longer authenticated, so the session resets. A 'stale' verdict means the rejection was earned by a token that has since been superseded (for example by a concurrent refresh), so it carries no information about the current session and the write simply retries.

Optimistic rollback

A rejected write reverts only its own optimistic value. Other pending writes for the same or different keys stay applied. The resulting error snapshot's data field carries the rolled-back value, so consumers reading the snapshot after the rejection see the pre-write state for that key.

Error snapshot and onError

The user-state snapshot exposes data (the current value, reflecting any rollback) alongside error (the SkyStateError that caused the most recent rejection, if any). In the React SDK, onError fires once per rejected or protocol (committed-unverified) error, passing the SkyStateError so the application can react without polling the snapshot.