Skip to content

_healthCheck CLOSED branch doesn't clear _hiddenSince — causes double SSE reconnect #117

@ZechCodes

Description

@ZechCodes

Bug

When _healthCheck() finds the EventSource in CLOSED state, it creates a new EventSource but returns before clearing _hiddenSince. This causes a second reconnect when both visibilitychange and focus fire on tab return.

Steps to reproduce

  1. Open a page with persistConnection: true
  2. Switch to another tab/app for >30 seconds (so _hiddenSince exceeds the 30s threshold)
  3. The browser may silently close the EventSource while the tab is hidden
  4. Return to the tab

What happens

Both visibilitychange (visible) and focus call _onPageVisible()_healthCheck():

  1. First call (visibilitychange): _es.readyState === CLOSED_es = null_connect() creates new ES → returns early before _hiddenSince = null
  2. Second call (focus): new ES exists, not CLOSED → checks _hiddenSince → stale value > 30s → _disconnect() kills the just-created ES → _connect() creates another ES

This double-connect causes two "connected" status events, which downstream code (in our case, E2EE session management) interprets as two reconnects, triggering duplicate teardown+reinit cycles.

Fix

Clear _hiddenSince in the CLOSED branch before returning:

_healthCheck() {
    if (!this._es) {
        this._connect();
        return;
    }

    if (this._es.readyState === EventSource.CLOSED) {
        this._hiddenSince = null;  // ← Add this line
        this._es = null;
        this._connect();
        return;
    }

    const hiddenDuration = this._hiddenSince
        ? Date.now() - this._hiddenSince
        : 0;
    this._hiddenSince = null;

    if (hiddenDuration > 30000) {
        this._disconnect();
        this._connect();
    }
}

Workaround

We're currently monkey-patching _healthCheck from the consuming app. Would love to remove that once this is fixed upstream.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions