Skip to content

Fix 8 race condition bugs in internal-session.js#3084

Open
lifeart wants to merge 2 commits intomainmatter:masterfrom
lifeart:fix/internal-session-race-conditions
Open

Fix 8 race condition bugs in internal-session.js#3084
lifeart wants to merge 2 commits intomainmatter:masterfrom
lifeart:fix/internal-session-race-conditions

Conversation

@lifeart
Copy link

@lifeart lifeart commented Mar 18, 2026

Summary

Fixes 8 race condition bugs in internal-session.js where this.authenticator can become null during asynchronous operations such as token refresh, multi-tab session sync, and concurrent logout flows.

Closes #3083

Changes

  1. _lookupAuthenticator() — Returns null early when the authenticator name is falsy, instead of attempting a container lookup with an invalid key.

  2. invalidate() — Guards against this.authenticator being null or the lookup failing. Falls back to _clear(true) directly so the session still gets properly invalidated.

  3. restore() — Wraps the authenticator lookup in try/catch and checks for null. If the authenticator can't be found, logs a debug message and clears the session with the restored content.

  4. _bindToStoreEvents() — Wraps the authenticator lookup in try/catch and checks for null. If lookup fails, resets _busy and clears the session with content.

  5. _bindToAuthenticatorEvents() — Removes existing event listeners before adding new ones to prevent listener accumulation across rapid re-authentication cycles. Also guards against null authenticator.

  6. _clear() — Unbinds authenticator events (sessionDataUpdated, sessionDataInvalidated) before clearing session state. This prevents stale listeners from firing after the session has been invalidated.

  7. _updateStore() — Returns a resolved promise when isAuthenticated is true but this.authenticator is empty, preventing the store from persisting session data without an authenticator key.

  8. _onSessionDataUpdated() — Returns early when this.authenticator is null, preventing _setup() from being called with a null authenticator during concurrent data update events.

Test plan

  • Added unit tests for all race condition guards in internal-session-test.js
  • Verify existing test suite still passes
  • Manual testing: rapidly switch between tabs with authenticated sessions
  • Manual testing: trigger token refresh while simultaneously logging out

🤖 Generated with Claude Code

lifeart and others added 2 commits March 18, 2026 15:13
…sion

Address 8 race condition bugs where `this.authenticator` can be null
during async operations (token refresh, multi-tab sync, logout):

1. _lookupAuthenticator() — return null for falsy authenticator name
2. invalidate() — guard against null/missing authenticator
3. restore() — guard authenticator lookup with try/catch and null check
4. _bindToStoreEvents() — guard authenticator lookup with try/catch
5. _bindToAuthenticatorEvents() — remove old listeners before adding new
6. _clear() — unbind authenticator events before clearing session state
7. _updateStore() — prevent persisting without authenticator key
8. _onSessionDataUpdated() — guard against null authenticator

Closes mainmatter#3083

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ve test specificity

- Add debug() logging in _clear() and _bindToAuthenticatorEvents() catch blocks
  that previously swallowed errors silently
- Add debug() log in _updateStore() when skipping persist due to empty authenticator
- Improve _bindToAuthenticatorEvents test to also assert listeners are not added
- Strengthen invalidate race-condition tests with precondition checks and
  assert.step/verifySteps pattern instead of broad try/catch

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

_updateStore() can persist session without authenticator key, causing silent logout on next restore

1 participant