fix: prevent stale FetchUser from clobbering the current user's identity#1672
Merged
Conversation
When an on-new-session Fetch User for a previous user (e.g. a cached anonymous user) is still pending and login() switches the current user, the in-flight Fetch User completing cleared and re-hydrated local data unconditionally — wiping the external_id login had just set on the new current user. The subsequent user-2 (409) conflict recovery then built a Create User with external_id: nil, minting a stray anonymous user and silently dropping the login (and any addEmail/tags applied afterward). In executeFetchUserRequest, early-return when the fetched user is no longer the current user: such a response is stale, so clearing/hydrating local data would only clobber the now-current user. Add regression tests for both the non-current (must not touch current user) and current (must still clear + hydrate) cases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fadi-george
approved these changes
Jun 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
One Line Summary
Stop a stale Fetch User call from clearing the current user's data, which was dropping the
external_idset bylogin()and orphaning subsequent properties updates on that user.Details
Motivation
Reported by a customer who added the SDK in an app update, then called
login+addEmail, and sometimes the emails landed on the wrong anonymous user and the login silently failed.Root cause is an ordering race in
OSUserExecutor: when an on-new-sessionOSRequestFetchUserfor a previous user (a cached anonymous user) is still pending andlogin()switches the current user, the in-flight Fetch User's success handler calledclearUserData()unconditionally, wiping theexternal_idthatlogin()had just set on the new current user. The following user-2 (409) conflict recovery then built anOSRequestCreateUserwithexternal_id: nil, minting a stray anonymous user and dropping the login.#1669 (the prewarm fix) makes this reproduction now very hard to hit (by deferring user creation until after first unlock it restores the normal launch ordering where no on-new-session Fetch User races ahead of login) but it does address the root issue. The stale-fetch clobber still occurs any time a Fetch User is pending ahead of a login()-driven request (e.g. a programmatic login() right after initialize, or slow network), no prewarm required.
Scope
executeFetchUserRequestnow early-returns when the fetched user is no longer the current user, which is safe, a stale response is discarded rather than clearing/hydrating the now-current user.parseFetchUserResponseis unchanged, as it is also used by the Create User path that remains unaffected.Testing
Unit testing
Added two regression tests in
UserExecutorTests:testFetchUser_forNonCurrentUser_doesNotClearCurrentUserData— a stale fetch must not clear the current user (fails before this fix, passes after).testFetchUser_forCurrentUser_stillClearsStaleData— the normal current-user fetch still clears + hydrates.Full
UserExecutorTestsandSwitchUserIntegrationTestspass.Manual testing
The original bug was captured in repro logs (FetchUser running ahead of IdentifyUser →
CreateUser with external_id: nil). The race is now reproduced deterministically by the unit test above; reproducing the prewarm-triggered timing reliably on-device is impractical.Affected code checklist
Checklist
Overview
Testing
Final pass
🤖 Generated with Claude Code