# Bug 246605: Offline Writes

- Status: open
- Project: Zero
- Creator: @aboodman
- Created: 2026-02-07T16:59:52Z
- Modified: 2026-10-19T19:01:31Z
- Reactions: 👀 ×3 (Max-Levitskiy, Stanton-B, joodaloop) · 👍️ ×2 (Max-Levitskiy, danielcompton)
- URL: https://bugs.rocicorp.dev/p/zero/issue/246605

## Description

Zero doesn't currently support offline writes. This bug tracks the known technical blockers.

## Mutation Recovery

When a write happens while `connecting`, it is written to local storage and replayed on next connection – even across app restarts.

However, if that app restart gets a new version of Zero, or a new schema version, Zero creates a new local storage area and the offline write is effectively lost - marooned in the old storage area.

In Replicache, this was solved with a system called "mutation recovery" that rescued these mutations from prior schema/Zero versions. This system needs to be ported to Zero.

## Slow connection with lots of offline writes

When Zero starts, it sends all queued mutations to the server and simultaneously pulls updates from the server. When the server updates are applied, any queued mutations are "rebased" (re-run locally) to resolve conflicts.

If there are many locally queued changes, this process can be arbitrarily slow.

To support long periods offline, Zero should probably have a mode where it first sends all queued mutations, then replays them after the server updates are applied. But this would conflict with the goal to startup and get server updates fast when there aren't many queued mutations, so some work would be needed to balance these two goals.

## Offline Mutations Must Never Be Rejected

When a mutator throws an error on the server, the handleMutateRequest function in `@rocicorp/zero/server` returns the error to the client but marks the mutation processed.

This is a good UX and DX when online because it mirrors the behavior of normal web apps.

When offline, mutations should never be rejected. Otherwise the user could lose arbitrarily large amounts of work. Mutations should be replayed until they can be accepted.

This would require a different implementation of `handleMutateRequest`. Also the developer would have to be very careful to respond to any erroring mutations quickly, as those clients will be unable to save work until the error is resolved. There may be additional product work required to make this work well.

## Mutation should probably be opt-in offline capable on a case-by-case basis

Making the entire app work well fully offline is probably unrealistic for most apps. It feels more likely that people will succeed by making subsets of their apps work offline. Even major apps work this way to day -- you can "like" a post on FB while offline but can't compose or edit.

## Comments (5)

### @G2Jose — 2026-02-22T19:55:36Z

I really wish this feature existed, because it's a hard blocker for my app. I currently use powersync and really like it, but I'd love to try out Zero because of its ability to resolve queries synchronously on the very next frame.

Reactions: ❤️ ×3 (Stanton-B, aboodman, devagrawal09)

### @aboodman — 2026-02-23T17:09:23Z

I understand and appreciate that. Unfortunately, we can’t do everything and have chosen to focus on online for the foreseeable future.

You might want to check out livestore which focuses on offline and synchronous writes.

### @vinayakyadav2709 — 2026-03-23T07:17:24Z

See how others are doing the it? there must be solutions right?

### @vinayakyadav2709 — 2026-03-23T08:02:35Z

so are we implementing this? can you share some resources or theory so I can try implementing some solution

### @Max-Levitskiy — 2026-10-19T19:01:31Z

guys, do you accept contributions?
in general, that feature is acceptable for many cases, as you not always need to "resolve hard conflicts". In reality, I think it's a minority of the situations. And for majority of situations should be handled on the application level.
For example, we use zero for makelocal.eu. And we think of cases, when someone has a 3d printer farm in basement, go with phone, do a lot of operations to update statuses of orders, swap filaments, etc. and it can be a situation when no connection in the basement.
For such case, we need to build an additional workaround with additional layer to store the data, then sync it to zero when back online(or switch to alternatives).
And, in reality, there's no difference with merges, if you have 1 day, 1 minute (like in zero), or 10ms lag. If you have a REAL concurrency problem (especially, connected with security), you'll need to handle and solve it.
I would suggest you some solution, like a couple of simple strategies, like "latest timestamp wins" (trust client), "first come - first serve" and "reject on conflict" with "app dev have to handle the resolution it by themself" ("diff-style" handler, "are you sure you want to override changes", or reject completely usecases).
