Description
The reconnect() method in WebSocketFactory is unreachable due to a JavaScript truthiness bug with the connectionAttempt sentinel value. Even when reconnect: true is passed in options, websocket connections that are closed by the server are never automatically re-established.
The Bug
connectionAttempt is initialized to -1 as a sentinel meaning "no active reconnect timer":
private connectionAttempt = -1;
The reconnect() method guards against duplicate reconnect attempts:
private async reconnect(): Promise<void> {
if (this.connectionAttempt || this.state === WebSocketState.DESTROYED) {
return;
}
// ... reconnect logic ...
}
In JavaScript, -1 is truthy. This guard always evaluates to true, so reconnect() returns immediately without ever reaching the reconnection logic — including the check for this.options.reconnect.
The onclose handler calls this.reconnect(), and onopen resets connectionAttempt back to -1. So every close/reconnect cycle hits the same dead path.
Impact
Any consumer using WebSocketFactory with { reconnect: true } (including k8sWatch in @openshift/dynamic-plugin-sdk-utils, which sets this as a default) will never recover from a server-side close. The websocket silently dies and is never re-established. Consumers relying on watch semantics for real-time updates will stop receiving events permanently until the user triggers a full page reload or re-navigation.
Suggested Fix
Option A — Change the sentinel to a falsy value (recommended):
Replace all occurrences of this.connectionAttempt = -1 with this.connectionAttempt = 0, and update the initializer:
private connectionAttempt = 0;
0 is falsy in JavaScript and is never returned by window.setTimeout (which returns positive integers), making it a correct sentinel for "no active timer."
Option B — Use an explicit comparison in the guard:
if (this.connectionAttempt > 0 || this.state === WebSocketState.DESTROYED) {
Affected File
packages/lib-utils/src/web-socket/WebSocketFactory.ts
Version
Confirmed in @openshift/dynamic-plugin-sdk@5.0.1 / @openshift/dynamic-plugin-sdk-utils@5.0.1. The bug appears to be present on main as well based on the current source.
Description
The
reconnect()method inWebSocketFactoryis unreachable due to a JavaScript truthiness bug with theconnectionAttemptsentinel value. Even whenreconnect: trueis passed in options, websocket connections that are closed by the server are never automatically re-established.The Bug
connectionAttemptis initialized to-1as a sentinel meaning "no active reconnect timer":The
reconnect()method guards against duplicate reconnect attempts:In JavaScript,
-1is truthy. This guard always evaluates totrue, soreconnect()returns immediately without ever reaching the reconnection logic — including the check forthis.options.reconnect.The
onclosehandler callsthis.reconnect(), andonopenresetsconnectionAttemptback to-1. So every close/reconnect cycle hits the same dead path.Impact
Any consumer using
WebSocketFactorywith{ reconnect: true }(includingk8sWatchin@openshift/dynamic-plugin-sdk-utils, which sets this as a default) will never recover from a server-side close. The websocket silently dies and is never re-established. Consumers relying on watch semantics for real-time updates will stop receiving events permanently until the user triggers a full page reload or re-navigation.Suggested Fix
Option A — Change the sentinel to a falsy value (recommended):
Replace all occurrences of
this.connectionAttempt = -1withthis.connectionAttempt = 0, and update the initializer:0is falsy in JavaScript and is never returned bywindow.setTimeout(which returns positive integers), making it a correct sentinel for "no active timer."Option B — Use an explicit comparison in the guard:
Affected File
packages/lib-utils/src/web-socket/WebSocketFactory.tsVersion
Confirmed in
@openshift/dynamic-plugin-sdk@5.0.1/@openshift/dynamic-plugin-sdk-utils@5.0.1. The bug appears to be present onmainas well based on the current source.