block/buzz · error · Error

Relay publish was superseded by a session change.

Error message

Relay publish was superseded by a session change.

What it means

relayClientSession compares the caller's connection generation with the session's current generation right before sending raw JSON over the Tauri websocket plugin. If the connection was re-established (generation bumped) or wsId became null, the payload would go over a stale socket, so it throws. This is a stale-socket fence for publishes.

Source

Thrown at desktop/src/shared/api/relayClientSession.ts:656

  }

  private async sendRaw(payload: unknown[]) {
    if (this.wsId === null) {
      throw new Error("Relay socket is not connected.");
    }

    await invoke("plugin:websocket|send", {
      id: this.wsId,
      message: {
        type: "Text",
        data: JSON.stringify(payload),
      },
    });
  }

  private async sendRawForGeneration(payload: unknown[], generation: number) {
    if (generation !== this.connectionGeneration || this.wsId === null) {
      throw new Error("Relay publish was superseded by a session change.");
    }
    const wsId = this.wsId;
    await invoke("plugin:websocket|send", {
      id: wsId,
      message: { type: "Text", data: JSON.stringify(payload) },
    });
  }

  private normalizeRelayError(error: unknown, fallbackMessage: string) {
    return error instanceof Error ? error : new Error(fallbackMessage);
  }

  private recoverFromSocketFailure(
    error: unknown,
    fallbackMessage: string,
  ): Error {
    const normalizedError = this.normalizeRelayError(error, fallbackMessage);
    this.resetConnection(normalizedError);

View on GitHub (pinned to 6c35e82bd5)

Solutions

  1. Catch the error and re-publish using a freshly obtained generation (e.g. via session.reconnect()).
  2. Avoid holding publishes across community switches; cancel or re-dispatch them after the switch.
  3. Serialize publishes through the session so each uses its current generation.
  4. Surface a retry affordance to the user for failed outbound messages.

Example fix

// before
try { await send(payload, oldGeneration); } catch {}
// after
try { await send(payload, oldGeneration); }
catch (e) {
  if (isSuperseded(e)) { const gen = await session.reconnect(); await send(payload, gen); }
  else throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

// verify session generation freshness before send
if (generation !== session.currentGeneration()) await session.reconnect();

Type guard

function isSupersededError(e: unknown): boolean { return e instanceof Error && e.message.includes('superseded by a session change'); }

Try / catch

try { await publishEvent(ev); } catch (e) { if (isSupersededError(e)) await publishEvent(ev); else throw e; }

Prevention

When it happens

Trigger: sendRawForGeneration invoked with a generation captured before a reconnect/community switch completed; concurrent publish racing a session teardown that nulled wsId.

Common situations: Community switch while a publish is in flight; automatic reconnect after network blip; publish queued during rate-limit wait then sent on a replaced socket.

Related errors


AI-assisted analysis of block/buzz@6c35e82bd5 (2026-09-05). Data as JSON: /api/errors/3e9184a48f8e2fc7. Report an issue: GitHub.