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
After a failed send, publishSessionEvent attempts one reconnect and re-send. Before re-sending it re-validates three fences: ownership is unchanged, the session generation equals the generation returned by session.reconnect(), and this event is still the pending one. Any mismatch means the publish was superseded by a newer session or the event was removed from pending, so it throws instead of double-sending.
Source
Thrown at desktop/src/shared/api/relayEventPublisher.ts:64
session.pendingEvents.get(event.id) !== pendingEvent
) {
return;
}
// Expected socket recovery must not reject the operation being retried.
session.pendingEvents.delete(event.id);
const sendError = session.recoverSocketFailure(error, sendErrorMessage);
session.pendingEvents.set(event.id, pendingEvent);
let retryGeneration: number | null = null;
try {
retryGeneration = await session.reconnect();
if (
publishOwnership !== session.ownership() ||
session.generation() !== retryGeneration ||
session.pendingEvents.get(event.id) !== pendingEvent
) {
throw new Error(
"Relay publish was superseded by a session change.",
);
}
await session.send(["EVENT", event], retryGeneration);
} catch (retryError) {
if (session.pendingEvents.get(event.id) !== pendingEvent) return;
window.clearTimeout(timeout);
session.pendingEvents.delete(event.id);
reject(
publishOwnership === session.ownership() &&
retryGeneration !== null &&
session.generation() === retryGeneration
? session.recoverSocketFailure(retryError, sendError.message)
: session.normalizeError(retryError, sendError.message),
);
}
});View on GitHub (pinned to dad5a33865)
Solutions
- Treat as terminal for this attempt: re-create the publish on the current session (new event state) rather than retrying in place.
- Catch and check session.ownership()/generation() before any manual re-send.
- Avoid triggering multiple reconnects concurrently; route reconnection through the session.
- Verify the message wasn't actually delivered before re-publishing to prevent duplicates.
Example fix
// before
await publishEvent(event); // retried blindly after failure
// after
try { await publishEvent(event); }
catch (e) {
if (isSuperseded(e)) return; // publish was superseded; do not double-send
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
if (session.pendingEvents.get(ev.id) === undefined) return; // already resolved/removed; don't retry
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)) return; /* do not double-send */ throw e; }
Prevention
- Only one reconnect owner per session
- Deduplicate publishes by event id
- Check actual delivery before re-publishing
When it happens
Trigger: Retry path raced a second community switch or reconnect (ownership or generation changed), or pendingEvents no longer contains this event (it was cleared/committed elsewhere) between reconnect and re-send.
Common situations: Rapid community switches during a flaky connection; duplicate reconnects triggered concurrently; event already removed from pendingEvents by a timeout or teardown while the retry was in flight.
Related errors
- repo announcement lost community serving lease: {error}
- Read-only relay socket is not connected.
- Relay disconnected for community switch.
- Relay publish was superseded by a session change.
- an admin cannot ban or time out a community owner or fellow
AI-assisted analysis of block/buzz@dad5a33865 (2026-09-05).
Data as JSON: /api/errors/d04379d3017b2ebf.
Report an issue: GitHub.