Skip to content

Fix session expiration handling and keychain fallback logic#88

Merged
jancurn merged 5 commits intomainfrom
claude/fix-lint-e2e-issues-8bmCr
Mar 18, 2026
Merged

Fix session expiration handling and keychain fallback logic#88
jancurn merged 5 commits intomainfrom
claude/fix-lint-e2e-issues-8bmCr

Conversation

@jancurn
Copy link
Member

@jancurn jancurn commented Mar 18, 2026

Summary

This PR improves session expiration handling in the bridge process and adds a keychain fallback mechanism to handle cases where the OS keychain is unavailable for writes but file-based storage is used.

Key Changes

Bridge Process Session Expiration Handling

  • Made handlePossibleExpiration async: Changed from synchronous to asynchronous to properly await session status updates
  • Refactored session status updates: Instead of calling markSessionStatusAndExit which chains multiple async operations, now directly calls updateSession to synchronously update the session status, making it immediately visible to the CLI
  • Improved shutdown sequencing: Uses setImmediate to schedule shutdown after the error response is sent, ensuring the CLI receives the response before the process exits
  • Fixed error handling order: In forwardMcpRequest, now calls handlePossibleExpiration before sendError so session status is updated before the error response is sent to the CLI

Keychain Fallback Logic

  • Added file-based fallback in keychainGet: When the OS keychain returns null but is available, now also checks the file-based fallback storage
  • Handles mixed storage scenarios: Addresses cases where another process (e.g., CLI) may have stored credentials in file-based storage because OS keychain writes failed while reads silently return null

Implementation Details

  • The session status update now happens synchronously (via updateSession) rather than through a chained async operation, ensuring the status is persisted and visible to the CLI immediately
  • The shutdown is deferred using setImmediate to allow the error response to be sent before the process terminates
  • The keychain fallback only triggers when the OS keychain is available but returned null, preventing unnecessary file I/O when the keychain is truly unavailable

https://claude.ai/code/session_01S5dYbXc9Numzts5NtQRTxc

claude and others added 5 commits March 17, 2026 22:07
Two fixes:

1. keychain.ts: When OS keychain getPassword() returns null (entry not
   found) without throwing, also check the file-based fallback. This
   fixes a cross-process issue where the CLI stores credentials in the
   file (because keychain setPassword throws) but the bridge reads from
   the OS keychain (getPassword succeeds with null) and never checks
   the file. Fixes proxy bearer token authentication returning 400
   instead of 401.

2. bridge/index.ts: Make handlePossibleExpiration async and await the
   session status update before sending the error response to the CLI.
   Previously the status update was fire-and-forget, causing a race
   where the CLI would check sessions.json before the bridge had
   written the 'expired' status, resulting in 'crashed' instead.
   Schedule shutdown via setImmediate so the error response is sent
   first.

https://claude.ai/code/session_01S5dYbXc9Numzts5NtQRTxc
setImmediate fires in the very next event loop iteration, which may not
give enough time for the socket.write() in sendError to flush to the
client before shutdown tears down the socket. A 100ms setTimeout
provides a safer margin.

https://claude.ai/code/session_01S5dYbXc9Numzts5NtQRTxc
…tion

The previous approach detected keychain availability lazily per-operation,
which could cause split-brain: writes go to file fallback while reads
succeed from keychain (returning null), leading to stale data forever.

Now we probe with a test write+read+delete on first use. If both work,
keychain is used for everything; otherwise file fallback is used. This
also removes the band-aid fileGet fallback in keychainGet.

https://claude.ai/code/session_01S5dYbXc9Numzts5NtQRTxc
Ensures getPassword returns the exact value we set, and uses a unique
key per probe to avoid conflicts with concurrent mcpc instances.

https://claude.ai/code/session_01S5dYbXc9Numzts5NtQRTxc
@jancurn jancurn merged commit fd297dd into main Mar 18, 2026
6 checks passed
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.

3 participants