Skip to content

No automatic retry on transient write failures — pending edits stranded, never resynced #25

Description

@TrystonPerry

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

  1. useCollection / useDocument, make an edit so localState is pending.
  2. Force the batch.commit() / setDoc / updateDoc to reject once (e.g. drop the network for the autosave interval, or stub a transient unavailable).
  3. 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.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions