#5596·vueuse

useEventSource: onopen/onerror/onmessage never check if the connection was superseded, unlike useWebSocket after #5573

Author: MILLERMARRUCreated Aug 18, 2026Updated Aug 18, 2026

Describe the bug

useEventSource's _init() sets up es.onopen, es.onerror, and es.onmessage (plus the per-named-event listeners) without ever checking whether es is still the current connection (eventSource.value === es). useWebSocket just got exactly this guard added to its last unguarded handler in #5573 (onopen/onclose already had it, onmessage was the gap) — useEventSource has none of the three.

Reproduction

packages/core/useEventSource/index.ts, current _init():

const es = new EventSource(urlRef.value, { withCredentials })
status.value = 'CONNECTING'
eventSource.value = es

es.onopen = () => {
  status.value = 'OPEN'
  error.value = null
}

es.onerror = (e) => {
  status.value = 'CLOSED'
  error.value = e
  // ... reconnect logic calls setTimeout(_init, delay), creating a new `es`
}

es.onmessage = (e: MessageEvent) => {
  event.value = null
  data.value = serializer.read(e.data) ?? null
  lastEventId.value = e.lastEventId
}

None of these three handlers (nor the useEventListener(es, event_name, ...) registered per named event a few lines below) check eventSource.value === es before writing to status/error/data/event/lastEventId.

Because _init() is re-entrant (called again by open() when the reactive url changes, and by the autoReconnect path via setTimeout(_init, delay)), it's possible for an old EventSource (es) to still have an in-flight onmessage/onerror queued when a new one is created and assigned to eventSource.value. That stale event still fires and overwrites data/status/error with a value from a connection that's already superseded, exactly the class of bug #5573 fixed for useWebSocket's onmessage (see its PR description: "A reconnect closes the old socket and swaps in a new one, and any message the old socket still delivers before its close handshake finishes ends up writing to data").

Expected behavior

Same guard useWebSocket now has on all three of its equivalent handlers, applied to useEventSource's onopen/onerror/onmessage and the named-event listeners:

es.onmessage = (e: MessageEvent) => {
  if (eventSource.value !== es)
    return
  event.value = null
  data.value = serializer.read(e.data) ?? null
  lastEventId.value = e.lastEventId
}

System Info

Checked against current main (packages/core/useEventSource/index.ts), compared with useWebSocket's fix in #5573.

Additional context

useWebSocket and useEventSource share the same "reconnect swaps the underlying connection object" shape, so I'd expect the fix to look basically identical to #5573's diff, just applied to EventSource's three handlers plus the dynamic useEventListener loop instead of WebSocket's.