Troubleshooting
Start with sync.status.lastError for sync failures. For one rejected record, inspect its metadata with sync.debug.getRecordMeta(sync.todos, id).
Validation error
status.lastError.reason === "validation"
A local or server value does not match the schema. Check for:
- Undeclared fields.
- Values that cannot be stored as JSON.
- A missing collection
id. - Zod refinements that reject default values.
Network error
status.lastError.reason === "network"
The client could not reach the sync endpoint, the response was not ok, or no fetch implementation was available.
Dirty operations stay pending. The client schedules a retry while data remains dirty.
Authentication error
status.lastError.reason === "auth"
The sync endpoint returned 401 or 403. Refresh auth state, reauthenticate, or clear user-local data when switching accounts.
Conflict error
status.lastError.reason === "conflict"
The edit was based on an older version of the record. The current v1 behavior is to:
- Keep the local edited value.
- Record the conflict in metadata.
- Stop retrying the rejected operation.
Use debug.getRecordMeta(collection, id) to inspect conflict details.
Pending ops are empty after a rejection
An operation rejected by the server is treated as handled, so it is removed from pending operations. This applies to:
- Validation errors.
- Access denials.
- Conflicts.
- Missing records.
- Server-error rejections.
The local edited value may remain with lastError metadata, but the operation is no longer dirty.
A test sees no pending operation after an edit
Local proxy writes are batched. Use fake timers to advance 100 ms when needed, then call await sync.flush() before reading debug.getPendingOps().
Data appears under the wrong user
Construct the client with a stable per-user default adapter, such as
storage: { namespace: 'my-app:user_1' }, then call await sync.hydrate(). The namespace scopes
IndexedDB, local storage, session storage, and BroadcastChannel state.
Clear local data
Use:
await sync.clearLocalData()
This clears:
- Synced records and account state.
- Device state.
- Session state.
It also notifies other tabs in the same namespace.
Keep secrets out of local state
Synced records, device, and session are stored in browser-managed persistence. Treat them as user data caches, not secure secret storage.