Skip to main content

no-floating-network-connections

Require browser network connection handles to be retained so they can be closed.

Rule catalog ID: R009

Targeted pattern scopeโ€‹

This rule targets browser connection constructors with explicit .close() lifecycle APIs:

  • WebSocket
  • EventSource
  • window.WebSocket, self.WebSocket, and globalThis.WebSocket
  • window.EventSource, self.EventSource, and globalThis.EventSource

The rule reports connection instances that are immediately discarded or chained directly into a method call other than .close(). In both cases there is no remaining connection handle available for teardown.

What this rule reportsโ€‹

The rule reports:

  • standalone connection construction such as new WebSocket(url);
  • voided connection construction such as void new EventSource(url);
  • immediate send or listener chains such as new WebSocket(url).send(message);
  • immediate EventSource listener registration on a discarded instance

It intentionally does not require same-function close() calls. Ownership can be transferred to a component instance, connection manager, returned value, or longer-lived runtime owner.

Why this rule existsโ€‹

WebSocket and EventSource create long-lived network connections. If the handle is not retained, code cannot reliably call .close(), remove listeners, or coordinate reconnect and shutdown behavior. Discarding the handle makes the connection lifecycle implicit and usually leaks work until the page or runtime exits.

Incorrectโ€‹

new WebSocket("wss://example.com/socket");
void new EventSource("/events");
new WebSocket(url).send("hello");
new EventSource("/events").addEventListener("message", onMessage);

Correctโ€‹

const socket = new WebSocket("wss://example.com/socket");

socket.addEventListener("message", onMessage);
socket.close();
const source = new EventSource("/events");

source.addEventListener("message", onMessage);
source.close();
return new WebSocket(url);
registerConnection(new EventSource(url));

Behavior and migration notesโ€‹

Store the connection handle in the owner that will close it. For UI code, that is usually the component or route lifecycle. For shared libraries, returning the connection or passing it to a connection manager makes the ownership contract explicit.

This rule does not autofix. Introducing a variable without a matching close path would hide the lifecycle problem instead of solving it.

ESLint flat config exampleโ€‹

import runtimeCleanup from "eslint-plugin-runtime-cleanup";

export default [
runtimeCleanup.configs.recommended,
];

When not to use itโ€‹

Do not enable this rule for code that intentionally creates page-lifetime connections and does not need explicit teardown. Prefer a narrow disable comment with a reason when a connection is meant to live for the whole runtime.

Further readingโ€‹