Repository navigation
Conversation
szuperaz
marked this pull request as ready for review
October 9, 2026 16:28
szuperaz
requested review from
MartinCupela,
isekovanic and
oliverlaz
as code owners
October 9, 2026 16:28
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.
https://linear.app/stream/issue/REACT-1111/offline-queue-resent-messages-never-clear-the-failed-local-copy
Context
Backend team found (ticket) that the default JS request timeout is the same as the default backend write timeout: 3secs. This is what can happen:
message.newevent arrives AFTER the client-side timeout rejection, all is good, the message will move toreceivedstatemessage.newthe SDKs' own error handler will flip the state back tofailedmessage id already existserror from backend. Android and iOS SDKs already handles this by returning the local message and swallowing the error. We were asked by the backend team to handle it as well. React SDK already handles this, but RN didn't.sendMessageencounters the specific "id already exists" error, we swallow it and instead return a fake backend response from the local message (we strip all local-only fields/transforms)How to reproduce
On web I'm able to reproduce it with using Chrome's network throttling (3g) and sending multiple messages in close proximity in time. (On RN Claude just set up a proxy)
RN + offline mode
In RN when offline mode is enabled, this bug can happen more frequently because
sendMessage(and all offline-ready API methods) follow this logic:offlineDb.queueTaskI assume this is some fallback mechanism to shield us from offline DB errors, but this means that:
message.new, so we end up in the failed stateAfter the fix, the fallback send will resolve with the local message. However, since at that point the offline DB already added a pending task, the fix also removes this pending task (v10 doesn't have this logic for removing the pending task, see the replay logic in that case in the next paragraph).
Despite removing the pending resend task in the use-case detailed above, it's still possible that RN ends up with a resend task and gets a "duplicate id error" when it replays message send: from what I was told by Claude RN will replay offline tasks without waiting for the offline DB to be loaded into JS state. And JS's
sendMessagewill only swallow the "duplicate id error" if that message is stored locally. Worst-case scenario here is the user clicks resend, and the new "duplicate id" handler flips the message to received state. (on v10 the flip to received state happens automatically as long as the failed message is part of the state we fetch from backend on reconnect/next app start)Remaining issues
failedstate, one withreceived. Verified by Claude with a unit test. Solved in v10threadinstance, instead ofchannel.state threadsfailed thread reply state is never updated from HTTP response, this is true for any kind of failure. Solved in v10A note about the connected GH ticket
The backend team automatically connected the #1838 ticket. It's not really clear how the bug in that ticket works (and the customer didn't answer to Ivan's questions). There is a chance that maybe they hit the issue of "losing connection before receiving backend reply" since they mention going offline? But even if their issue is the offline path, that shouldn't be a permanent failed state anymore after the fix. So it is safe to close that ticket.