Skip to content

Fix cold start loading hang on Desktop/iOS - #484

Merged
2witstudios merged 3 commits into
masterfrom
fix/cold-start-loading-hang
Feb 7, 2026
Merged

2witstudios merged 3 commits into
masterfrom
fix/cold-start-loading-hang

Conversation

@2witstudios

@2witstudios 2witstudios commented Feb 7, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Fix auth timing race condition that causes sidebar and content area to hang on loading skeletons during cold start on Desktop (Electron) and iOS (Capacitor)
  • Add safety-net timeout for bearer token retrieval (IPC/Keychain) to prevent indefinite hangs
  • Set session cookies for iOS in login and refresh routes, matching existing desktop behavior

Root Cause

On cold start, there's a 1-render window where CenterPanel mounts and SWR fires BEFORE loadSession() can set isLoading=true and unmount it. On web this is harmless (cookies are synchronous), but on Desktop/iOS the fetchWithAuth awaits IPC/Keychain with no timeout — hanging forever.

Changes

File Change
stores/useAuthStore.ts Initial isLoading: true (not persisted — only affects cold starts)
hooks/useAuth.ts Explicit setLoading(false) when no session check needed
lib/auth/auth-fetch.ts getSessionTokenWithTimeout() with 3s timeout for bearer token retrieval
api/auth/mobile/login/route.ts Set session cookie for iOS (was desktop-only)
api/auth/mobile/refresh/route.ts Add appendSessionCookie to response
api/auth/device/refresh/route.ts Set session cookie for iOS (was desktop-only)
hooks/useBreadcrumbs.ts Add errorRetryCount: 3, errorRetryInterval: 2000 to SWR config

Test plan

  • Desktop cold start: Launch Electron → navigate to a page → page tree and breadcrumbs load immediately (no 12s hang)
  • iOS cold start: Launch Capacitor → navigate → same behavior
  • Web unaffected: Web app works as before (isLoading starts true but clears within 1 render cycle)
  • iOS session cookie: After iOS login, check response headers for Set-Cookie: session=...
  • Auth store tests pass: 66/66 passing
  • Login → navigate → logout → login cycle works on all platforms

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • iOS devices now properly support session cookie handling during authentication flows
  • Bug Fixes

    • Fixed token retrieval timeouts preventing hung requests on initial load
    • Fixed loading state not clearing when session load is skipped
  • Improvements

    • Added automatic retry mechanism for breadcrumb data fetches
    • Enhanced mobile refresh authentication flow with centralized header management

Close auth timing race condition where CenterPanel mounts and SWR fires
before loadSession() can block the UI, causing hung requests on
Desktop (IPC) and iOS (Keychain) platforms.

Changes:
- Set initial isLoading to true in auth store (not persisted, so only
  affects cold starts) to prevent premature CenterPanel mount
- Add else branch in useAuth to explicitly clear isLoading when no
  session check is needed, unblocking the UI
- Add 3s timeout to bearer token retrieval in fetchWithAuth as safety
  net against hung IPC/Keychain calls
- Set session cookie for iOS in mobile login and device refresh routes
  (matching existing desktop behavior)
- Add appendSessionCookie to mobile refresh response
- Add SWR error retry config to useBreadcrumbs (errorRetryCount: 3)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Feb 7, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@2witstudios has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 23 minutes and 16 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

This PR extends iOS device support in authentication flows to match desktop behavior, adds a timeout mechanism for token retrieval to prevent hangs on cold starts, improves error handling with SWR retry configuration, and adjusts UI loading state initialization to ensure proper UI unblocking.

Changes

Cohort / File(s) Summary
iOS Session Cookie Handling
apps/web/src/app/api/auth/device/refresh/route.ts, apps/web/src/app/api/auth/mobile/login/route.ts, apps/web/src/app/api/auth/mobile/refresh/route.ts
Extended session cookie appending logic to include iOS platforms alongside desktop, and centralized headers object construction in mobile refresh endpoint.
Token Retrieval Timeout Mechanism
apps/web/src/lib/auth/auth-fetch.ts
Added TOKEN_RETRIEVAL_TIMEOUT_MS constant and getSessionTokenWithTimeout() helper to prevent hung bearer-token requests by racing token fetch against a timeout, returning null on timeout and logging a warning.
SWR Error Retry Configuration
apps/web/src/hooks/useBreadcrumbs.ts
Introduced errorRetryCount: 3 and errorRetryInterval: 2000ms to SWR fetcher options for automatic retry behavior on fetch errors.
UI Loading State Management
apps/web/src/hooks/useAuth.ts, apps/web/src/stores/useAuthStore.ts
Modified useAuth effect to unblock UI by calling setLoading(false) when session load is not required, and changed initial isLoading state in Auth store from false to true.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • PageSpace#453: Modifies the same auth endpoints (device refresh, mobile login/refresh) for session cookie handling changes, directly related to iOS platform expansion.
  • PageSpace#220: Changes the same auth subsystems (auth-fetch.ts, useAuth.ts) for token and session management improvements.
  • PageSpace#225: Modifies auth-fetch.ts and session refresh flows with related token retrieval and refresh behavior updates.

Poem

🐰 iOS hops the desktop path with glee,
Token timeouts ward off hung requests with spree,
SWR retries fetch breadcrumbs anew,
Loading states bloom when auth sees through,
Auth flows strengthen, smoother than dew! 🌿

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main problem being fixed: a cold start loading hang on Desktop/iOS platforms. It matches the comprehensive PR objectives which detail fixing an auth timing race condition and related issues on these platforms.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/cold-start-loading-hang

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@apps/web/src/lib/auth/auth-fetch.ts`:
- Around line 924-943: The getSessionTokenWithTimeout function currently starts
a setTimeout that logs a warning but never clears it, causing false timeout
warnings when tokenPromise resolves first; update getSessionTokenWithTimeout to
store the timer id (from setTimeout), and when tokenPromise resolves or rejects
first clearTimeout(timerId) before returning the token (or null), ensuring the
timeout callback is canceled; keep use of TOKEN_RETRIEVAL_TIMEOUT_MS and ensure
both branches (this.getSessionFromElectron() and storage.getSessionToken()) are
wrapped so clearing the timer happens on success or failure to avoid spurious
logs.

Comment thread apps/web/src/lib/auth/auth-fetch.ts
2witstudios and others added 2 commits February 7, 2026 10:36
The session cookie should only be set for desktop and iOS platforms
(which need it for Next.js middleware), not unconditionally for all
platforms including Android.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Prevents orphaned setTimeout when the token promise wins the race
in getSessionTokenWithTimeout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@2witstudios

Copy link
Copy Markdown
Owner Author

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@2witstudios
2witstudios merged commit fe130c3 into master Feb 7, 2026
9 of 10 checks passed
2witstudios added a commit that referenced this pull request Feb 7, 2026
Add missing appendSessionCookie mock to @/lib/auth in mobile-refresh tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@2witstudios
2witstudios deleted the fix/cold-start-loading-hang branch March 11, 2026 03:13
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.

1 participant