Messenger WebSocket (/ws/msg/) appears to close (1006) when the mobile tab is hidden, and does not seem to reconnectUp front: we might be wrong about some of the details below (hopefully not), and where we are unsure we have said so. But the socket closing is a real issue, we hit it repeatedly and a few other people have mentioned running into it too.
Feature: Built-in messenger
Endpoint (observed): wss://xmrbazaar.com/ws/msg/
Impact: On mobile, realtime messaging seems to stop after the phone sleeps or the user switches apps, and does not appear to come back on its own.
What we are confident about: this is recurring and repeatable. The messenger connection drops and does not come back on its own after the mobile tab is hidden; we have hit it many times in normal use, a few others have mentioned the same thing, and we reproduced it deliberately below.
What we could check in code: the client-side messenger script that ships to the browser (/js/messenger/websocket.js and actions.js) is public, so the parts about what the client does on close are read straight from that code, not inferred. Those are marked below as from the client code.
What is still guesswork: the cause of the close itself, the timing, and anything server-side. We cannot see what closes the socket, only how the browser behaves around it, captured over chrome://inspect on a real phone. You will have a much better view of the server side than we do.
What we sawWith the messenger open and a conversation active on mobile, if the tab goes into the background (switching apps, or the screen turning off), the WebSocket seems to close a few seconds later with code 1006. On returning to the tab, it looks like no new connection is made. The messenger shows "Error: Websocket connection closed", and the conversation shows "No connection. Please copy your draft message first, then refresh the page to reconnect." In our runs a full page reload was the only thing that brought it back, though we did not test every recovery path.
LogsThree runs on the messenger page with a conversation open, logging the live messenger_globals.socket events and tab visibility, over chrome://inspect against a phone.
A couple of caveats on the method, since they affect how to read these. chrome://inspect keeps showing the last frame on the desktop while the phone tab is actually hidden, so a recording can look foreground when the tab was backgrounded; we are trusting the VISIBILITY log lines over the image. Mobile browsers also seem to suspend timers in a hidden tab, so the final export printed on resume rather than at the close; the per-event timestamps should still reflect when each event fired, but we cannot fully vouch for timer accuracy across the suspend.
The three run event logs (start, message, visibility, and close events with timestamps) are in the attached logs folder: run1-app-switch, run2-screen-off, run3-foreground-idle, plus a 30-minute trollbox control run. Runs 1 to 3 are transcribed from the console shown in the screen recordings, faithful to what was on screen but not the raw export files; the raw exports and the control run are in the folder. The key values are stated in the reading below.
How we read these (best guess)This is inference from three runs, not a diagnosis. Take it as a starting point for someone who can see the logs and code.
The close seems tied to the tab being hidden rather than to the socket being idle. Runs 1 and 2 both closed 1006 roughly five seconds after VISIBILITY hidden (about 5.0s and 5.2s). Run 3 stayed open for 150 seconds in the foreground with only occasional frames and did not close in that window. That is only three runs, so we would not call it conclusive, but it is consistent.
The repeated ~5s gap between hidden and close in the two drop runs might suggest a fixed timeout somewhere on that socket's path, as opposed to random network loss. That is a guess worth checking against server timeouts, not something we can confirm from outside.
In the runs where it closed, we did not see an OPEN event after the tab became visible again, and readyState stayed 3. That is what makes us think it is not reconnecting on its own, but we are only inferring that from the absence of a log line, and we have not seen whatever reconnect logic may or may not exist.
The endpoint reads as wss://xmrbazaar.com/ws/msg/ in the START line of all three runs, so that part we are fairly confident about.
Also seen, weaker evidenceThe same "connection closed" state seems to show up on the public trollbox too, which uses a different socket (wss://xmrbazaar.com/ws/tb/). We did not instrument that with the same logging, so it is an impression, not proof. If it turns out both sockets behave the same way, it might point to shared connection handling rather than anything specific to the messenger, but that is speculation at this stage.
We have also hit what looks like the same closure during ordinary use while going back and forth between the site and another app, including, from memory and not captured, while filling in a listing. That lines up with the hidden-tab pattern above, but we are noting it as a recollection, not evidence.
From the client code (public script, read directly)These are read from the shipped websocket.js / actions.js, so they are not guesses, though you would still know the intent behind them better than we do. They line up with why a close stays broken on return:
- onclose only calls show_error(... 'Websocket connection closed'). It does not open a new socket, schedule a retry, or re-init. So once the socket is gone, nothing in the client brings it back.
- There is no visibilitychange, pageshow, online, or focus handler in the client. Nothing runs when the tab comes back to the foreground, which fits what the logs showed: no OPEN after the tab returns.
- onerror and ondisconnect also just show an error. (ondisconnect is not a real WebSocket event, so that one never fires at all.)
- Synchronous sends have a 60s timeout via Promise.race, but the timeout branch does not delete the entry from ws_response_queue, so a timed-out request leaves its resolver stuck. Minor, and not the cause of the close, just noting it while it was in view.
- The ping keepalive in actions.js reschedules only after a successful response and has no foreground-resume hook, so it does not restart a socket that died while hidden.
Source files, readable live:
websocket.jsactions.jsPut together, this is why a real close stays broken when the user returns: the socket closes (cause unknown to us), and the client has no path to reopen it.
If reconnect-on-resume were added, the shape would be something like the following, shown only to convey the idea:
function ensureMessengerConnection() {
const s = messenger_globals.socket;
if (!s || s.readyState !== WebSocket.OPEN) connectMessenger();
}
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') ensureMessengerConnection();
});
The part we cannot see (server side, still a guess)What actually closes the socket about five seconds after the tab goes hidden is not visible to us. It could be a proxy or backend idle timeout on a backgrounded connection, the mobile browser itself, or something else. If a backgrounded or quiet socket is being closed on purpose, that may well be fine, and client-side reconnect on resume would still be what smooths it over for the user. This is the part where you can see what we cannot.
Unrelated, just noticedThe console also showed Uncaught SyntaxError: missing ) after argument list (service-worker.js:193) and a failed ServiceWorker registration on load. Probably nothing to do with the socket, but flagging it since it was right there.
https://drive.proton.me/urls/RFAJSG423M#MJuo5SJnMOwF