Skip to content

fix(auth): do not await PowerSync disconnect during session clear - #1313

Merged
rolandgeider merged 1 commit into
wger-project:masterfrom
bentucker:fix/session-clear-deadlock
Aug 13, 2026
Merged

fix(auth): do not await PowerSync disconnect during session clear#1313
rolandgeider merged 1 commit into
wger-project:masterfrom
bentucker:fix/session-clear-deadlock

Conversation

@bentucker

Copy link
Copy Markdown
Contributor

Proposed Changes

  • Stop awaiting the PowerSync disconnect in the keep-data session reset
    (clearSessionOnly). The await closes a three-party circular wait when a
    refresh token is rejected: the refresh future runs the session teardown,
    the teardown awaits PowerSync's disconnect, the disconnect waits for the
    in-flight sync fetch to abort, and that fetch is awaiting the same
    single-flight refresh future through AuthHttpClient's pre-emptive refresh.
    The refresh future then never completes and every later authenticated
    request queues behind it forever (15 second timeouts on every endpoint,
    surviving app restarts). See the issue for the full production diagnosis.
  • The disconnect is now fire-and-forget on this path. The DB is deliberately
    kept, so there is no wipe to race with: once the cleared state is
    published, the wedged sync fetch resumes without a credential, gets a 401
    and PowerSync backs off, letting the deferred disconnect finish. The wipe
    path (explicit logout) keeps its wipe-before-publish ordering.
  • Make the disconnect hook injectable (visibleForTesting field) so the
    regression test can install a never-completing disconnect;
    PowerSyncDatabase is a base class and cannot be faked directly.
  • Add a regression test: a rejected refresh must complete and publish the
    logged-out state even when the PowerSync disconnect hangs forever.

Related Issue(s)

Closes #1312

Checklist

  • Tests pass locally (flutter test, 1235 tests)
  • flutter analyze reports no issues on the changed files
  • New behaviour is covered by a regression test

Comment thread lib/core/network/auth_notifier.dart
A rejected refresh token runs clearSessionOnly inside the single-flight
refresh future. PowerSync's disconnect can block on an in-flight sync
fetch that is itself awaiting that same refresh future through
AuthHttpClient's pre-emptive refresh, closing a three-party cycle
(refresh -> disconnect -> sync fetch -> refresh) that permanently wedges
every authenticated request behind the never-completing refresh. The
session-expired message never appears because it is sequenced after the
hanging call, and an app restart re-triggers the same rejected refresh
during startup revalidation, so the deadlock re-forms immediately.

On the keep-data path the DB is not wiped, so there is no re-attach race
to guard against; the disconnect is only a courtesy stop of the sync
loop and is now fire-and-forget. The wipe path (explicit logout) keeps
its ordering. The disconnect hook is injectable for tests because
PowerSyncDatabase is a base class that cannot be faked.
@bentucker
bentucker force-pushed the fix/session-clear-deadlock branch from d3fc354 to 57fe126 Compare August 13, 2026 00:08
@rolandgeider
rolandgeider merged commit e6c703c into wger-project:master Aug 13, 2026
13 checks passed
bentucker pushed a commit to bentucker/flutter that referenced this pull request Aug 13, 2026
Persists a device-local active-workout pointer (routine, day, iteration,
cursor, validity window) so an OS-killed app can resume gym mode where it
left off, surfaced through a dashboard resume card that deep links back
into gym mode. The pointer is cleared on explicit session save and on any
local-data wipe (logout, user switch) so another user never inherits a
stale in-progress workout.

Also stamps a reused day-less workout session with the routine day at log
time; upstream sets the day on newly created sessions (6eb6197) but the
reuse branch left existing sessions day-less, which stalls log-driven
scheduling (Day.need_logs_to_advance) on the backend.

Rebased onto the lib/core + lib/features layout; the session-clear
deadlock fix formerly on this branch was merged upstream (wger-project#1313) and the
gym-mode day stamping half became redundant with upstream's dayId work.
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.

Rejected token refresh deadlocks the app: every request times out until re-login is impossible

2 participants