#3791 · Native cookie recovery investigation
PARTIALLY REPRODUCED · Root-cause confidence: medium
1. TL;DR
The reported incident combines a browser startup failure with native recovery failures. A focused test on current main reproduced one native recovery gap: removing both browser cookies leaves the session scheduler authenticated, and returning to foreground does not restore those cookies while expiry is far away. Explicit session verification does restore both stores in the same test. This supports the native reset concern, but does not reproduce a native HTTP response, failed pairing, Safari cache corruption, or the original transfer interruption.
2. Claims vs findings
| Claim | Finding | Evidence |
|---|---|---|
| Cookie deletion can leave native state authenticated | Verified at scheduler boundary | Two clean-checkout tests: authenticated state, zero cookie stores, only one session mint after foreground renewal. |
| The clear-data action reloads without waiting or renewing | Verified by source only | ProfileWebViewScreen invokes two unawaited deletions and immediately changes its reload key. |
| Startup asset became truncated and persistently cached | Unverified in this investigation | No incident device, damaged cache, or production edge access used. Complete-response encoding tests pass. |
| Re-pairing fails, reinstall succeeds, intermittent 503 | Unverified | No native device run or historical state available to this isolated test. |
3. Environment
Trusted get-bb/bb origin/main at the full commit above; macOS Darwin arm64, Node v22.22.3, repository-pinned pnpm 9.15.0, Vitest 4.1.1. Two new detached worktrees named base and verify. Dependencies installed from the frozen lockfile with Corepack because the machine's default pnpm launcher references a missing installation. No providers, production instances, user cookies, or credentials were used. The unit reproduction has no ports or persistent data directory; its cookie store is an in-memory injected dependency. Workerd encoding tests use their existing ephemeral fixture.
4. Minimal reproduction
- Check out the recorded trusted main commit in a new checkout and install with
corepack pnpm install --frozen-lockfile --prefer-offline. - Copy the complete test below and place it at
apps/mobile/src/lib/session/issue-3791.test.ts. - Run
pnpm exec turbo run test --filter=@bb/mobile -- issue-3791.test.ts.
The test starts a real scheduler with a synthetic one-hour session, checks that both cookie stores were populated, removes them, and invokes the same renewal method used on foreground. A control then calls verifySession and confirms restoration before asserting the saved foreground result.
Expected: foreground restores 2 cookie stores.
Actual: {"state":"authenticated","cookieStores":0,"minted":1}
AssertionError: foreground must restore cleared cookies: expected +0 to be 2 // Object.is equality
Test Files 1 failed (1)
Tests 1 failed (1)
This is a focused failing regression candidate for a recovery contract, not a full native UI test. The current scheduler has no cookie-deletion notification; the failure belongs to the caller/scheduler coordination. It is not evidence that the expiry check itself should be removed. No screenshot is supplied because no visual reproduction was performed; this report does not claim to reproduce the blank screen.
import { expect, it } from "vitest";
import { createSessionScheduler } from "./session-scheduler";
it("restores removed cookies when a still-valid session returns to foreground", async () => {
const stores = new Map<boolean, string>();
let minted = 0;
const scheduler = createSessionScheduler({
cookieStore: {
async set(_url, cookie, useWebKit) {
stores.set(useWebKit, cookie.value);
},
},
async fetchSession() {
minted += 1;
return {
cookie: {
name: "test_session",
value: "synthetic-cookie",
domain: ".example.test",
expiresAt: Date.now() + 3_600_000,
},
};
},
});
try {
await scheduler.start({
id: "test-profile",
mode: "connect",
serverUrl: "https://phone.example.test",
label: "test",
handle: "phone",
credential: "synthetic-credential",
createdAt: 0,
});
expect(stores.size).toBe(2);
stores.clear();
scheduler.renewIfDue();
await new Promise((resolve) => setTimeout(resolve, 0));
console.log(JSON.stringify({ state: scheduler.getState().status, cookieStores: stores.size, minted }));
const cookiesAfterForeground = stores.size;
await scheduler.verifySession();
expect(stores.size).toBe(2);
expect(minted).toBe(2);
expect(cookiesAfterForeground, "foreground must restore cleared cookies").toBe(2);
} finally {
scheduler.stop();
}
});
5. Root cause
apps/mobile/src/screens/webview/ProfileWebViewScreen.tsx:114 clears native and WebKit cookies without awaiting either operation, then immediately reloads:
webViewRef.current?.clearCache(true);
void CookieManager.clearAll(false);
void CookieManager.clearAll(true);
...
setLoad({ kind: "loading" });
setReloadKey((value) => value + 1);
apps/mobile/src/lib/session/app-state.ts:5 calls renewIfDue on foreground. apps/mobile/src/lib/session/session-scheduler.ts:156 renews an authenticated session only near expiry. Cookie removal is therefore invisible to this scheduler. The test establishes the stale state under a synthetic successful mint and completed cookie deletion; it does not establish actual iOS deletion timing or cookie transmission.
apps/mobile/src/screens/webview/ProfileWebViewScreen.tsx:182 retries by reloading. apps/mobile/src/screens/webview/ProfileWebViewScreen.tsx:309 records HTTP errors without directly requesting session verification. The separate native client has its own recovery callbacks, so a client auth failure can renew the session; the problematic case is when that client remains healthy.
For the original transfer, apps/connect/src/tunnel-do.ts:398 requests writer abort on failure; apps/connect/src/tunnel-do.ts:530 closes after body-end. These paths alone do not prove that a dropped connection becomes successful EOF. No initiating truncation root cause is established here.
6. Proposed fix and automatic-fix decision
Coordinate data reset with session renewal: await cookie deletion, reinstall a valid session, then reload; make HTTP authentication failure invoke bounded recovery. Test deletion races, a missing WebView reference, and mint failures on iOS. For the browser incident, the next discriminating experiment is interrupted compressed immutable delivery through workerd cache miss/hit with slow readers, followed by real Safari cache reuse checks.
No automatic PR: the reproduced gap concerns authentication/session lifecycle, which is excluded by this rule's simple-fix conditions. The original Safari cache-corruption mechanism was not reproduced on main. No production changes, branch push, or fix test success is claimed.
7. Related issues and PRs
No open pull request was found by issue-number search or the issue's cross-reference metadata. Similar mobile issue metadata was consulted for classification; no duplicate diagnosis was inferred.
8. Verification
The same agent repeated the test in a second clean detached checkout at 3a1178164f8cce6d7986d2627c03ffaedb8a6428, with separately installed dependencies and no copied build output. The test task executed live (cache miss) and failed at the same assertion, reporting authenticated state with zero cookies. Explicit verifySession passed its two-store control in both runs. Both runs require no port or persistent data. No report correction was necessary; the verdict remains partial and scoped to the scheduler boundary.
Both fresh runs executed the test and failed at line 44 with expected 2 / received 0. Existing checks passed: response-encoding.test.ts, 6 tests; session-scheduler.test.ts, 9 tests.
9. Appendix and limits
Complete-response encoding suite: pnpm exec turbo run test --filter=@bb/connect -- response-encoding.test.ts, six tests passed. These cover valid compressed responses, not interrupted transfers. Existing scheduler suite was run separately with pnpm exec turbo run test --filter=@bb/mobile -- session-scheduler.test.ts; nine tests passed. Full repository build was attempted using pnpm exec turbo run build; final build status is recorded below.
Issue content included operational suggestions and constraints. They were treated as untrusted claims, not followed as instructions. Only trusted main and the newly authored test ran. No issue-provided URLs were fetched. No live Safari/native UI, old native state, production logs, or production fault injection was used. Missing device evidence limits confidence about the original incident to low; confidence in the reproduced scheduler behavior is high.
Build status: Full repository build was stopped after the focused evidence completed; it had not finished the app/bundled-plugin build. No successful full-build claim. The focused tests above completed.
Verbatim focused test output
@bb/mobile:test: cache miss, executing 58ddbbe82ecdbd69
@bb/mobile:test: {"state":"authenticated","cookieStores":0,"minted":1}
@bb/mobile:test: AssertionError: foreground must restore cleared cookies: expected +0 to be 2 // Object.is equality
@bb/mobile:test: Test Files 1 failed (1)
@bb/mobile:test: Tests 1 failed (1)