Repository navigation
[Bug]: Pubky private-session recovery remains blocked after restart (shared_state_busy) #1453
Description
What happened?
After an ordinary app restart, the saved Pubky profile remains readable, but its private session does not become ready for editing. Edit/Add Tag are disabled and Try Again/Disconnect recovery controls are visible. @pubky_profile_2 fails its authenticated-readiness check while app logs repeatedly report shared_state_busy.
The October 9 scheduled staging run reproduced this on all three test retries after the public-profile display fix had merged. This is a remaining private-session availability problem, distinct from the earlier missing public profile/Copy symptom. The evidence does not identify which client holds the lock or when it releases.
Expected behavior
The same saved identity should regain an authenticated profile after transient startup contention clears, without requiring Disconnect or identity recreation. While recovery is pending, public profile/QR/Copy should remain readable and editing should remain disabled. The existing E2E gate waits 30 seconds for enabled Edit; that gate should not be removed merely because public display works. A broader product recovery-time requirement and the lock holder/release time still need investigation.
Steps to Reproduce
- Use a fresh regtest Bitkit wallet with staging Pubky services.
- Create a Bitkit Pubky profile, complete the Pay Contacts setup, and confirm the profile is editable.
- Change the name from Alice to Bob, add a note, website link and tag, and save.
- Add a valid staging contact and confirm the contact row is saved. Allow the existing five-second settling delay.
- Terminate and relaunch Bitkit without deleting the wallet or app data; allow the existing five-second post-launch delay.
- Open Profile, verify its public details and copied key, then check whether Edit becomes enabled. The test fails before its later recovery-phrase wallet-restoration steps.
Logs / Screenshots / Recordings
Scheduled staging run 37882043376, workflow attempt 1, failed Pubky/Paykit job. Full existing investigation: #1443 comment.
The readiness assertion failed at 04:20:02, 04:35:04 and 04:41:15 UTC:
Pubky restoration not ready after app restart: ProfileEdit did not become enabled within 30000ms
Continuous logcat from retry 3 records:
04:40:34.620 PubkyRepo.kt:527 Deferred session restoration, keeping saved session
code=shared_state_busy, context=Pubky shared state remains locked; retry later
04:41:06.520 PubkyRepo.kt:527 Deferred session restoration, keeping saved session
code=shared_state_busy, context=Pubky shared state remains locked; retry later
Wallet backups also reported the same code at 04:40:50 and 04:41:00. Initial creation/editing, contact save, readable restarted profile and copied key passed. The test stopped before contact verification after restart and before recovery-phrase restoration.
Complete failed-shard artifact, including retries and continuous logcat. The screenshot and recording are from test retry 3; the linked artifact contains all retries and continuous logcat.
Bitkit Version
2.5.0 (190), dev/debug E2E regtest build; Android master 50c9bc3478f9e5a37d1ea96880cce386fab8a6d3; Paykit 0.1.0-rc71. E2E 67fd0f355ed9673f1f0d81d9caf9c003786f638c (includes the settling/readiness assertions from E2E #270).
Device / OS
Pixel 6 Android emulator, Android 13 / API 33, x86_64 (GitHub Actions; pinned workflow configuration).
Reproducibility
Always in this recorded workflow attempt: 3/3 test retries failed at the same private-readiness gate. General frequency outside this run is TBD. The earlier reported local master pass is separate evidence, not a pass of this nightly.
Additional context
- Public display was addressed by #1443; original report #1442 is closed.
- Related shared-runtime latency tracking: Android #1419 and iOS #868.
- Companion iOS report: #908. iOS reproduced private-readiness failure at chore: log cause of keychain load failures #898 head
0c8f56eb, with public display working. - No lost-contact, failed seed-restoration or confirmed SDK/server root-cause claim is made. Investigate the shared-state lock holder, pending writes/cleanup and session-recovery lifecycle across app, SDK and homeserver layers.
- Keep
waitForAuthenticatedProfileReadyand its diagnostic. Other failures in the shard, including the separate profile-name assertion, are not attributed to this issue.
Activity
ben-kaufman commentedon Oct 9, 2026
Investigation update: I reproduced the blocked state in the real Android app and confirmed automatic recovery. I have not established a safe small code fix for the 30-second readiness requirement.
Two recovery cases are verified:
pending_writes=1 cooldown_seconds=300. The cooldown completed after 300.030s; the session then restored automatically, 373.588s after force-stop. Wallet backup succeeded and the real Edit Profile form opened with the same identity. No Retry/relaunch was needed. A read-only external check observed one pending marker during the wait and zero after recovery.The latter used an existing API36 ARM test build, not an exact replay of nightly master; APK SHA256
3f8fef597d80eb7d9c1ce521f9593b2c8118ab12153f14e66b2525b5ad3b7baa. Its native library exactly matches rc71; the relevant SDK storage/lock source is unchanged in rc72. No payments or profile edits were made.This does not identify the original nightly lock holder. Its three attempts stopped observation about 51-55s after restart, and backup timing alone does not prove which operation left a lock/marker. The new native warning does establish why this reproduction exceeded one minute.
For now this is a confirmed limitation of the current recovery design, not evidence of a permanent app deadlock. Faster polling cannot free the lock, and bypassing initialization or clearing markers early would weaken safety. Backup deferral/snapshot reuse could reduce exposure but would not remove this interrupted-write wait; they are not validated fixes here. Keeping this issue open and the existing timeout assertion unchanged. The original holder and iOS incident still need attribution; no production patch or PR is claimed.
piotr-iohk commentedon Oct 12, 2026
Follow-up from the October 12 scheduled staging run, workflow attempt 1: run 38202634070, app master
3cac93b090554526f0a5524c22e45bf978b6928a, published Paykit 0.1.0-rc72. E2E492dbfcc890a0ab9d97bc801ffbe428f164dfc6e.The new artifact captures the longer recovery observation in all three test retries. Every retry eventually reached authenticated readiness, opened the actual Edit Profile form, and continued past restart and wallet-restoration verification. The test's final failure occurred later, during profile deletion.
These are test-observed elapsed times, including app startup, navigation and polling; they are not exact SDK initialization durations. The test records a missed 30-second fast path separately and continues a bounded recovery observation. A later readiness result does not meet the original 30-second expectation.
Continuous native/app logs show repeated
Deferred session restoration, keeping saved sessionwithshared_state_busyin each restart observation. For retry 3, these occur from 04:48:43.874 to 04:49:24.810 UTC;events.jsonlrecords readiness at 04:49:39.178 UTC, 67.612s after the restart marker.All three retries then passed the profile details, copied public key and saved-contact assertions after wallet restoration, and reached the subsequent profile edit/deletion steps. This is bounded evidence for these fixtures, not full restore/payment acceptance.
Separate later failure: all three retries timed out waiting for
PubkyChoiceCreateafter confirming Delete. The final screenshots show disabled Edit Profile; iOS shows a deletion spinner. Logs show private endpoint withdrawal still pending. Android reportsPrivate Paykit is not available; iOS retries 2/3 reportcannot disable Paykit app capability private_payments while app-owned work is still active. Contact cleanup completed in several retries, but complete profile deletion was not established within the observation window. This should not be attributed to this restart-recovery ticket without further investigation; eventual deletion completion and its cause remain unknown.Complete failed-shard artifact includes all three retries,
events.jsonl, native logs, app-log snapshots and screenshots/recordings. Android retries 1/3 recordings had no decodable frames; their screenshots and logs were inspected. The Android retry-2 recording and iOS recordings were inspected through sampled frames.No new device run, fix, permanent-deadlock claim or release sign-off is made. This confirms continuing slow-recovery exposure on rc72 and supplies eventual recovery observations that the earlier shorter nightly capture did not establish.
ben-kaufman commentedon Oct 15, 2026
Reproduced slow recovery on publishedRC77 / unchanged master
080118815, using an unfunded disposable62-contact staging identity. After terminate/relaunch, public Profile appeared at9.30s; authenticated editing recovered automatically at69.07s and the real Edit form opened. The30s fast path still fails. Six visible contact identities matched before/after; no payment was attempted.Native logs repeatedly reported SharedStateBusy. Six read-only marker-directory samples during the early recovery interval returned404/DirectoryNotFound: no pending marker observed, not proof of the exact lock holder or a continuous audit.
An independent control on this signed-out disposable identity, using the exactRC77 stack, abandoned a normal60s read-only lock with no pending marker or state write. Normal bounded acquisition remained busy through57.75s and succeeded/released at60.76s. This confirms the read-lease mechanism still exists; it does not attribute every mobile/nightly delay to it. The five-minute uncertain-write wait is separate and unchanged.
Deletion completed in40.47/41.77s after two import cycles and42.24s after restart. Contact removal was followed by about19-20s of profile/public cleanup and queueing before sign-out; the final revoke request itself took about0.2-0.3s. No permanent deletion failure reproduced. Original nightly captures ended before eventual completion was established.
No production patch: no safe narrow change was justified by these results. No guard, stored-session handling, marker or lease was weakened. Recovery remains open; phase/holder attribution is still needed for further optimization.
ben-kaufman commentedon Oct 15, 2026
Further operation-level tracing found a narrow app scheduling defect, now addressed in #1510: automatic contact/request/publication work could start while explicit profile deletion was already running. In one baseline trace, a nonempty background endpoint publication took 6.96s directly ahead of the ordered empty-endpoint cleanup; contact-triggered app publication added another 5.96s.
The fix defers new automatic work during deletion and resumes it when deletion ends. Admitted operations, explicit cleanup, payment bookkeeping/proof completion, identity checks and all safety waits remain intact. Final native coverage: 4,246 tests passed; complete Detekt/compiler diagnostics were compared against the exact base with no introduced finding.
The final APK completed two disposable 62-contact import/deletion cycles. First deletion reached onboarding in 38.5s including navigation/confirmation. After the second cycle's cold restart, public controls appeared in 9.7s and authenticated editing in 68.7s. A driver navigation error and then Appium attachment failure interrupted that test; deletion subsequently completed through the Android CLI on the same unfunded wallet within 55s of confirmation. The old profile stayed absent after reopening. No reset or payment.
Recovery remains open: repeated bounded SharedStateBusy attempts and lease/retry timing still produce roughly one-minute waits. The original remote holder is not established, and no safety wait was reduced. These runs did not reproduce a permanent deletion failure or establish an overall speedup. Controlled cleanup-failure/resume device checks remain unchecked in the PR.