Bug: App hangs on splash screen after refresh token is rejected (400) on second refresh
Summary
After a successful login, the app performs one silent token refresh successfully, but the next refresh attempt is rejected by the server with 400 Bad Request. From that point on, the app hangs indefinitely on the splash screen and eventually becomes unresponsive / crashes, rather than falling back to a login prompt.
Environment
- App version: 2.0.4, build 250
- Platform: iOS
- Server: self-hosted,
docker.io/wger/server:latest (wger 2.6 alpha branch), via the official wger-project/docker compose stack
- Deployment: behind Nginx Proxy Manager → the bundled
wger-nginx container → web/powersync containers
- PowerSync self-hosted sync service in use (not the hosted wger.de sync)
Steps to reproduce
- Fresh login via username/password in the app against a self-hosted server.
- App performs an initial silent token refresh successfully.
- Wait roughly 4 minutes.
- App attempts another token refresh.
- Server responds
400 to /allauth/app/v1/tokens/refresh.
- App hangs on the splash screen and does not recover; eventually becomes unresponsive.
Actual behavior
Server logs show the clear pattern (nginx access log, timestamps ~4 minutes apart):
{"ts":"...14:38:22...","request_method":"POST","request_uri":"/allauth/app/v1/tokens/refresh",...,"status":200,"size":1459,...} {"ts":"...14:42:22...","request_method":"POST","request_uri":"/allauth/app/v1/tokens/refresh",...,"status":400,"size":15,...}
The first refresh succeeds; the second, ~4 minutes later, is rejected. After the 400, the app does not prompt for re-login — it hangs on the splash screen and eventually crashes.
Expected behavior
On a 400/invalid-refresh-token response, the app should clear the stored session and fall back to the login screen, rather than hanging indefinitely.
Suspected cause
This looks consistent with refresh token rotation: if the server issues a new refresh token on every successful refresh (invalidating the previous one) and the app isn't correctly persisting the newly-issued token from a successful refresh, the next refresh attempt would resend the now-invalidated token and get rejected. This reproduced independently in two separate sessions (once shortly after rotating the server's JWT signing keys — an expected one-time invalidation — and again afterward with a completely fresh login/token pair, ruling out the key rotation as the sole cause).
Additional notes
Reproducible and consistent across multiple attempts. Happy to provide additional logs (PowerSync/nginx/web) if useful.
Bug: App hangs on splash screen after refresh token is rejected (400) on second refresh
Summary
After a successful login, the app performs one silent token refresh successfully, but the next refresh attempt is rejected by the server with
400 Bad Request. From that point on, the app hangs indefinitely on the splash screen and eventually becomes unresponsive / crashes, rather than falling back to a login prompt.Environment
docker.io/wger/server:latest(wger 2.6 alpha branch), via the officialwger-project/dockercompose stackwger-nginxcontainer →web/powersynccontainersSteps to reproduce
400to/allauth/app/v1/tokens/refresh.Actual behavior
Server logs show the clear pattern (nginx access log, timestamps ~4 minutes apart):
{"ts":"...14:38:22...","request_method":"POST","request_uri":"/allauth/app/v1/tokens/refresh",...,"status":200,"size":1459,...} {"ts":"...14:42:22...","request_method":"POST","request_uri":"/allauth/app/v1/tokens/refresh",...,"status":400,"size":15,...}The first refresh succeeds; the second, ~4 minutes later, is rejected. After the
400, the app does not prompt for re-login — it hangs on the splash screen and eventually crashes.Expected behavior
On a
400/invalid-refresh-token response, the app should clear the stored session and fall back to the login screen, rather than hanging indefinitely.Suspected cause
This looks consistent with refresh token rotation: if the server issues a new refresh token on every successful refresh (invalidating the previous one) and the app isn't correctly persisting the newly-issued token from a successful refresh, the next refresh attempt would resend the now-invalidated token and get rejected. This reproduced independently in two separate sessions (once shortly after rotating the server's JWT signing keys — an expected one-time invalidation — and again afterward with a completely fresh login/token pair, ruling out the key rotation as the sole cause).
Additional notes
Reproducible and consistent across multiple attempts. Happy to provide additional logs (PowerSync/nginx/web) if useful.