fix(gateway): demote expected startup socket aborts (#104610)

* fix(gateway): demote expected startup socket aborts

* fix(gateway): retain queued-frame startup warnings
This commit is contained in:
Peter Steinberger
2026-07-11 11:27:21 -07:00
committed by GitHub
parent 5a5424474d
commit 7193d29c92
2 changed files with 58 additions and 1 deletions

View File

@@ -283,6 +283,46 @@ describe("attachGatewayWsConnectionHandler", () => {
expect(context).toMatchObject({ phase: "ws_upgrade_started" });
});
it("demotes local app startup aborts before the first frame", async () => {
const { socket, logWsControl } = await connectTestWs({
headers: { "user-agent": "OpenClaw/2607000290 CFNetwork/3860 Darwin/25" },
options: { isStartupPending: () => true },
});
socket.emit("close", 1006, Buffer.alloc(0));
expect(logWsControl.debug).toHaveBeenCalledWith(
expect.stringContaining("closed before connect"),
expect.objectContaining({ phase: "ws_upgrade_started" }),
);
expect(logWsControl.warn).not.toHaveBeenCalledWith(
expect.stringContaining("closed before connect"),
expect.anything(),
);
});
it("keeps queued local app startup frames at warning level", async () => {
const logWsControl = createGatewayWsTestLogger();
const { socket } = attachGatewayWsForTest({
attach: attachGatewayWsConnectionHandler,
headers: { "user-agent": "OpenClaw/2607000290 CFNetwork/3860 Darwin/25" },
options: { isStartupPending: () => true, logWsControl: logWsControl as never },
});
socket.emit("message", Buffer.from('{"type":"req","id":"queued"}'));
socket.emit("close", 1006, Buffer.alloc(0));
await waitForLazyMessageHandler();
expect(logWsControl.warn).toHaveBeenCalledWith(
expect.stringContaining("closed before connect"),
expect.objectContaining({ phase: "ws_upgrade_started" }),
);
expect(logWsControl.debug).not.toHaveBeenCalledWith(
expect.stringContaining("closed before connect"),
expect.anything(),
);
});
it("includes the last completed handshake phase on preauth timeout logs", async () => {
vi.useFakeTimers();
const { logWsControl } = await connectTestWs({

View File

@@ -273,6 +273,7 @@ export function attachGatewayWsConnectionHandler(params: AttachGatewayWsConnecti
const requestUserAgent = headerValue(upgradeReq.headers["user-agent"]);
const forwardedFor = headerValue(upgradeReq.headers["x-forwarded-for"]);
const realIp = headerValue(upgradeReq.headers["x-real-ip"]);
const openedDuringStartup = isStartupPending?.() === true;
const pluginNodeCapabilities = getPluginNodeCapabilities?.() ?? [];
const pluginSurfaceBaseUrl =
@@ -296,6 +297,11 @@ export function attachGatewayWsConnectionHandler(params: AttachGatewayWsConnecti
let lastFrameType: string | undefined;
let lastFrameMethod: string | undefined;
let lastFrameId: string | undefined;
let hasReceivedPreauthFrame = false;
socket.once("message", () => {
hasReceivedPreauthFrame = true;
});
const advanceHandshakePhase = (next: WsHandshakePhase) => {
if (WS_HANDSHAKE_PHASES.indexOf(next) > WS_HANDSHAKE_PHASES.indexOf(lastHandshakePhase)) {
@@ -421,6 +427,15 @@ export function attachGatewayWsConnectionHandler(params: AttachGatewayWsConnecti
normalizeLowercaseStringOrEmpty(userAgent).includes("swiftpm-testing-helper") &&
isLoopbackAddress(remote);
const isExpectedLocalAppStartupAbort = (code: number) =>
openedDuringStartup &&
code === 1006 &&
lastHandshakePhase === "ws_upgrade_started" &&
!hasReceivedPreauthFrame &&
lastFrameType === undefined &&
normalizeLowercaseStringOrEmpty(requestUserAgent).startsWith("openclaw/") &&
isLoopbackAddress(remoteAddr);
socket.once("close", (code, reason) => {
const durationMs = Date.now() - openedAt;
const logForwardedFor = sanitizeLogValue(forwardedFor);
@@ -452,7 +467,9 @@ export function attachGatewayWsConnectionHandler(params: AttachGatewayWsConnecti
const isExpectedStartupRetryClose =
closeCause === GATEWAY_STARTUP_PENDING_CLOSE_CAUSE && code === GATEWAY_STARTUP_CLOSE_CODE;
const logFn =
isNoisySwiftPmHelperClose(requestUserAgent, remoteAddr) || isExpectedStartupRetryClose
isNoisySwiftPmHelperClose(requestUserAgent, remoteAddr) ||
isExpectedStartupRetryClose ||
isExpectedLocalAppStartupAbort(code)
? logWsControl.debug
: logWsControl.warn;
const authReason = stringMetaValue(closeMeta, "authReason");