Skip to content

Fix: Remaining Kobo "Sync to last page read?" popup on sync#3607

Open
leahjessie wants to merge 2 commits into
janeczku:masterfrom
leahjessie:pr/kobo-popup-fix
Open

Fix: Remaining Kobo "Sync to last page read?" popup on sync#3607
leahjessie wants to merge 2 commits into
janeczku:masterfrom
leahjessie:pr/kobo-popup-fix

Conversation

@leahjessie
Copy link
Copy Markdown
Contributor

@leahjessie leahjessie commented Mar 28, 2026

Problem

When reading with a synced Kobo, users sometimes see a "Sync to last page read?" popup even though they've only been reading on that one device. The answer is always "no" and even when selecting "yes" it takes you to the same position. Previous fixes addressed related popup triggers but some still persisted. Fixes #3596 .

Background

The device uses GET /v1/library/<uuid>/state to fetch reading state when opening a book, and PUT to push progress updates while reading. GETs and PUTs are interspersed during a session; which one the session ends on appears to vary.
The popup decision happens on GET.

Two relevant timestamp fields on KoboReadingState:

  • LastModified (LM) - generated by the device, sent in every PUT body, stored on each sub-record (bookmark, statistics, read status) and propagated to the parent row.
  • PriorityTimestamp (PT) - conflict-detection field on the parent row only. Based on observed behavior, the device stores the PT from each GET response. If the next GET returns a newer PT, the device assumes another client made changes and could show the popup (e.g. the intended use case is syncing position across a Kobo device and the Kobo app, not something calibre-web supports.)

Observed via proxy: official Kobo cloud appears to always return PT = LM = the device's own LastModified, so the device always gets back a timestamp it recognizes.

Root cause

Our server was stamping PT with datetime.now() on every PUT. When the device next did a GET it saw a server-generated PT newer than its last confirmed one, which triggered the popup despite no real conflict or change on another client. If a session ended with a GET the device would confirm the new PT and the next open was fine; if it ended with a PUT, the next GET would show the mismatch and trigger the popup. That is why it was intermittent.

Fix

Commit 2 (core fix): Read LastModified from the PUT request body and use it as both LM and PT for all state saved in that request. The subsequent GET returns the device's own timestamp back, eliminating the mismatch. When state is changed outside a Kobo PUT, the flush hook falls back to datetime.now(), so PT still advances as before. Also fixes a minor issue where book_read.last_modified was being written on every PUT even when read status hadn't changed.

Commit 1 (prerequisite): Two things that would otherwise undermine commit 2:

  • priority_timestamp had its own onupdate=datetime.now() independent of LM, so PT and LM could drift apart, which is itself a popup trigger. Removed onupdate from PT; the flush hook now sets both fields together.
  • Commit 2d4ca23d added LM and PT to the PUT response UpdateResults to let the device confirm the new PT without a GET. Based on observed behavior, that does not appear to work. The device seems not to persist reading-state timestamps from PUT responses. This change removes that response data again and instead keeps PT/LM aligned through the normal GET path, matching observed official Kobo cloud behavior.

##Testing
I've been running this for a few weeks locally and have not seen the popup once.

@new-usemame
Copy link
Copy Markdown

Same flow landed in the community CWA-derived build Calibre-Web-NextGencps/kobo.py::HandleStateRequest mirrors back the device's own LastModified via g.kobo_reading_state_lm and stops bumping last_modified to server-now. docker pull ghcr.io/new-usemame/calibre-web-nextgen:latest. Thanks @leahjessie for the diagnosis + upstream fix. Note: NextGen is CWA-derived so it adds auto-ingest, kepubify, and KOReader sync on top of plain Calibre-Web — disclose if those aren't wanted.

leahjessie added a commit to leahjessie/calibre-web that referenced this pull request May 22, 2026
…otes/

- pr-kobo-popup-fix.md and pr-kobo-popup-fix-short.md: deleted.
  Submitted upstream as janeczku#3607.
- READER_SYNC_PLAN.md: moved into notes/ with a status header noting that
  most of the plan has shipped via feat/epub-reader-foliate (per-source
  reader_position, dual-write, Stage 1/2 arbitration, web Kobo offer popup,
  observability endpoints, config gate).
- notes/kobo-popup-attempts.md: cross-reference the upstream PR.

meta/ root is now scripts + AGENTS.md/CLAUDE.md/TODO.md + profiles/.
Investigation history and design plans live under notes/.
leahjessie added a commit to leahjessie/calibre-web that referenced this pull request May 22, 2026
…empts

The previous commit missed these because the modifications were unstaged
when git mv ran. Adds the 2026-05-21 status header to the plan and the
upstream PR (janeczku#3607) link in kobo-popup-attempts.md.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] kobo-sync: bogus "Sync to last page read?" popup after device auto-sleep

2 participants