Skip to content

fix(channels): overlapping loadChannels() — an older response can overwrite the newer region's list #154

Closed

Description

@dborup

Summary

Overlapping loadChannels() calls can render the wrong region's channel list: the response that lands last wins, not the request that was made last. Pre-existing on master; found while reviewing #153 (it does not make it worse).

Reproduction (seen in a browser against a local server)

  1. On #/channels, select region SJC and delay the /api/channels?region=SJC response by ~2.5 s.
  2. Before it returns, switch to SFO.
  3. The SFO response arrives first and renders; then the delayed SJC response arrives and replaces the list.
  4. The region filter shows SFO, but the sidebar shows the SJC set.

Same on master and on #153.

Where (master e829d1ea)

loadChannels() in public/channels.js (around line 1856) has no request generation; every caller (RegionFilter.onChange around line 1014, the show-encrypted toggle, shared-channel approval, init()) awaits api('/channels' + qs) and writes channels unconditionally.

Proposed fix

A request token like the existing messageRequestId / beginMessageRequest() / isStaleMessageRequest() pattern (around lines 179–245): bump a channelsRequestId when loadChannels() starts, and drop the response (no render, no reconcile) if a newer request has started meanwhile. Keep it compatible with #153's client-state merge (sample the WS sequence for the request that actually renders).

Acceptance criteria

  • Delayed older response + newer region change: the newer region's list is rendered and stays.
  • No extra /channels request per change; existing callers unchanged.
  • Unit test with controlled api() timing, plus a Playwright check with a delayed route.

Activity

  1. added a commit that references this issue on Oct 5, 2026
  2. dborup commented on Oct 5, 2026

    @dborup
    OwnerAuthor

    Fixed by #225 (merged as aff158c7).

    loadChannels() stamps each request with a generation (channelsRequestId), following the messageRequestId pattern. When a newer request has started, an older response is dropped, both on success and on error: it is neither rendered nor reconciled. No extra /channels request is made, and the #153 client-state merge is kept. A unit test with controlled api() timing and a Playwright test with a delayed route cover it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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