Skip to content

Library sync failures are invisible: empty shelf and failed fetch are indistinguishable #14

Description

@StaticHumStudio

The defect

ApiService.getLibraries() and getLibraryItems() return emptyList() on every failure path: non-2xx response, null body, and any thrown exception (timeout, parse failure). SyncManager.syncLibraries() then swallows exceptions with no logging.

The result is that four different situations produce one identical observable outcome, a blank shelf with nothing in the log:

  • the server returned an HTTP error
  • the request timed out
  • the response failed to parse
  • the library genuinely has no books

A user reporting "my shelf will not sync" cannot be told which one they hit, and neither can the log.

A second, user-facing variant: getLibraryItems paginates 100 items at a time and breaks out of the loop on a failed page. A fetch that dies on page 3 saves the first 200 books and reports complete success, so a silently truncated shelf looks like a complete one.

Why it matters beyond logging

The home screen's recently-played query is not scoped to a library, while the library grid is. Progress sync fetches in-progress items individually and succeeds cheaply. So when the library fetch fails, the user sees exactly the books they are partway through and nothing else, with no error anywhere. That is a confusing failure to be on the receiving end of and an unanswerable one to receive as a bug report.

Work already done

Branch diagnostics/sync-visibility (local, not pushed), commit 2eba33e:

  • RemoteResult with Ok / Partial / Failed, replacing the empty-list return, with logging on each failure branch. Partial covers the truncated-pagination case.
  • SyncManager records what the last sync produced, with counts and failure reason kept separate so an empty shelf never reads as a failure.
  • The in-app bug report body gains: app mode, selected library id, stored library and book counts (total, and within the selected library), and the last sync result.

25 new tests, 448 green. Verified on device: the report body renders the new fields correctly in Local mode.

Remaining

  • The last-sync record is in-memory only, so it resets to "never" on every app restart. It should be persisted alongside the other settings, otherwise most reports will say "never" regardless of what actually happened.
  • No surface evidence for the Audiobookshelf-connected path. The new fields are unit-tested there but have never been seen rendering on a device with a real server.
  • syncLibraries gained a CancellationException rethrow that is not test-backed. The same broad catch (Exception) still sits inside ApiService and was deliberately left alone.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions