Hotfix for the false-positive "Server unreachable" banner that landed in v2026.06.04's re-cut.
The single-failure flip-to-false produced two false-positive cases:
Startup race — AuthStore.baseUrl loads from Room asynchronously, so the first runOnce() can fire against AuthStore.DEFAULT_BASE_URL (http://localhost:8080) before the real server URL has hydrated. One failure was enough to lock the banner on for the next 5 minutes.
Deployments whose reverse proxy routes only /api/* to the Go server — /healthz never reaches the handler, so polls fail forever even though every real /api/* call succeeds. User sees "Server unreachable" permanently and OfflineGatedDataSource starts throwing OfflineException on every audio cache miss, silently breaking playback of uncached tracks.
Fix: require 3 consecutive failures (~15 min at the 5-min poll cadence) before flipping reachable=false; any single success resets the counter. Adds Timber.w/i at the flip transitions so operator logcat can diagnose genuine outages.
Test plan
Install on a device pointed at a working server → no false-positive banner during the first 5 minutes after launch.
Disable WiFi → Offline banner shows (driven by ConnectivityObserver, instant).
Re-enable WiFi → banner clears.
On deployments where /healthz is permanently unreachable but /api/* works → banner appears only after ~15 minutes of confirmed failures; logcat shows Timber.w "flipping reachable=false" at the transition.
Hotfix for the false-positive "Server unreachable" banner that landed in v2026.06.04's re-cut.
The single-failure flip-to-false produced two false-positive cases:
1. **Startup race** — `AuthStore.baseUrl` loads from Room asynchronously, so the first `runOnce()` can fire against `AuthStore.DEFAULT_BASE_URL` (`http://localhost:8080`) before the real server URL has hydrated. One failure was enough to lock the banner on for the next 5 minutes.
2. **Deployments whose reverse proxy routes only `/api/*` to the Go server** — `/healthz` never reaches the handler, so polls fail forever even though every real `/api/*` call succeeds. User sees "Server unreachable" permanently and `OfflineGatedDataSource` starts throwing `OfflineException` on every audio cache miss, silently breaking playback of uncached tracks.
Fix: require 3 consecutive failures (~15 min at the 5-min poll cadence) before flipping `reachable=false`; any single success resets the counter. Adds `Timber.w`/`i` at the flip transitions so operator logcat can diagnose genuine outages.
## Test plan
- [ ] Install on a device pointed at a working server → no false-positive banner during the first 5 minutes after launch.
- [ ] Disable WiFi → `Offline` banner shows (driven by `ConnectivityObserver`, instant).
- [ ] Re-enable WiFi → banner clears.
- [ ] On deployments where `/healthz` is permanently unreachable but `/api/*` works → banner appears only after ~15 minutes of confirmed failures; logcat shows `Timber.w "flipping reachable=false"` at the transition.
The single-failure flip-to-false produced two false-positive permanent
banner cases:
1. Startup race — AuthStore.baseUrl loads from Room asynchronously, so the
first runOnce() can fire against AuthStore.DEFAULT_BASE_URL
("http://localhost:8080") before the real server URL has hydrated. One
failure was enough to lock the banner on for the next 5 minutes.
2. Deployments whose reverse proxy routes only /api/* to the Go server —
/healthz never reaches the handler, so /healthz polls fail forever even
though every real /api/* call succeeds. User sees "Server unreachable"
permanently and OfflineGatedDataSource starts throwing OfflineException
on every audio cache miss, silently breaking playback of uncached
tracks.
Now we require 3 consecutive failures (~15 min at the 5-min poll cadence)
before flipping reachable=false, and any single success resets the
counter. Adds Timber.w/i at the flip transitions so operator logcat can
diagnose genuine outages.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Hotfix for the false-positive "Server unreachable" banner that landed in v2026.06.04's re-cut.
The single-failure flip-to-false produced two false-positive cases:
AuthStore.baseUrlloads from Room asynchronously, so the firstrunOnce()can fire againstAuthStore.DEFAULT_BASE_URL(http://localhost:8080) before the real server URL has hydrated. One failure was enough to lock the banner on for the next 5 minutes./api/*to the Go server —/healthznever reaches the handler, so polls fail forever even though every real/api/*call succeeds. User sees "Server unreachable" permanently andOfflineGatedDataSourcestarts throwingOfflineExceptionon every audio cache miss, silently breaking playback of uncached tracks.Fix: require 3 consecutive failures (~15 min at the 5-min poll cadence) before flipping
reachable=false; any single success resets the counter. AddsTimber.w/iat the flip transitions so operator logcat can diagnose genuine outages.Test plan
Offlinebanner shows (driven byConnectivityObserver, instant)./healthzis permanently unreachable but/api/*works → banner appears only after ~15 minutes of confirmed failures; logcat showsTimber.w "flipping reachable=false"at the transition.The single-failure flip-to-false produced two false-positive permanent banner cases: 1. Startup race — AuthStore.baseUrl loads from Room asynchronously, so the first runOnce() can fire against AuthStore.DEFAULT_BASE_URL ("http://localhost:8080") before the real server URL has hydrated. One failure was enough to lock the banner on for the next 5 minutes. 2. Deployments whose reverse proxy routes only /api/* to the Go server — /healthz never reaches the handler, so /healthz polls fail forever even though every real /api/* call succeeds. User sees "Server unreachable" permanently and OfflineGatedDataSource starts throwing OfflineException on every audio cache miss, silently breaking playback of uncached tracks. Now we require 3 consecutive failures (~15 min at the 5-min poll cadence) before flipping reachable=false, and any single success resets the counter. Adds Timber.w/i at the flip transitions so operator logcat can diagnose genuine outages.