Summary
When a collection or document write (sync()) fails, firestate sets error, clears inflightLocalState, and leaves localState pending without rescheduling a sync. There is no automatic retry, so a single transient failure (network blip, momentary unavailable, contention) strands the user's un-synced edits indefinitely — isSynced stays false and the change is never written until some unrelated later edit happens to reschedule the autosave.
Where
Compiled dist/index.mjs (v0.1.2):
- Collection
sync catch — sets state.error, waitingForUpdate = false, inflightLocalState = undefined, calls notify(), but does not reschedule autosave or re-attempt the batch.
- Document
sync catch — same pattern (both the delete branch and the set/update branch).
Repro
useCollection / useDocument, make an edit so localState is pending.
- Force the
batch.commit() / setDoc / updateDoc to reject once (e.g. drop the network for the autosave interval, or stub a transient unavailable).
- Restore the network.
Expected: firestate retries the write (bounded backoff) and the edit eventually syncs; isSynced returns to true.
Actual: the write is abandoned. localState stays set, isSynced stays false, and nothing retries. The edit only ever lands if the user makes another change later.
Impact
Silent data-durability gap. Downstream apps surface this as "my change wasn't saved" with no error at the moment of failure (only whatever onError toast the app wires up) and no recovery path. A navigation blocker keyed on isSynced will also get stuck.
Suggested fix
Bounded retry with backoff on retryable errors (mirror the listener's existing retryOnError/retryInterval config, or add maxWriteRetries). On a retryable failure, keep localState and scheduleAutosave() again rather than dropping the attempt; surface to onError only after retries are exhausted. (The pattern the prior hand-rolled hook used was MAX_SAVE_RETRIES + re-arming the autosave debounce.)
Summary
When a collection or document write (
sync()) fails, firestate setserror, clearsinflightLocalState, and leaveslocalStatepending without rescheduling a sync. There is no automatic retry, so a single transient failure (network blip, momentaryunavailable, contention) strands the user's un-synced edits indefinitely —isSyncedstaysfalseand the change is never written until some unrelated later edit happens to reschedule the autosave.Where
Compiled
dist/index.mjs(v0.1.2):synccatch — setsstate.error,waitingForUpdate = false,inflightLocalState = undefined, callsnotify(), but does not reschedule autosave or re-attempt the batch.synccatch — same pattern (both the delete branch and the set/update branch).Repro
useCollection/useDocument, make an edit solocalStateis pending.batch.commit()/setDoc/updateDocto reject once (e.g. drop the network for the autosave interval, or stub a transientunavailable).Expected: firestate retries the write (bounded backoff) and the edit eventually syncs;
isSyncedreturns totrue.Actual: the write is abandoned.
localStatestays set,isSyncedstaysfalse, and nothing retries. The edit only ever lands if the user makes another change later.Impact
Silent data-durability gap. Downstream apps surface this as "my change wasn't saved" with no error at the moment of failure (only whatever
onErrortoast the app wires up) and no recovery path. A navigation blocker keyed onisSyncedwill also get stuck.Suggested fix
Bounded retry with backoff on retryable errors (mirror the listener's existing
retryOnError/retryIntervalconfig, or addmaxWriteRetries). On a retryable failure, keeplocalStateandscheduleAutosave()again rather than dropping the attempt; surface toonErroronly after retries are exhausted. (The pattern the prior hand-rolled hook used wasMAX_SAVE_RETRIES+ re-arming the autosave debounce.)