fix(gateway): disarm wrapper timeout before teardown#70704
Conversation
🔒 Aisle Security AnalysisWe found 1 potential security issue(s) in this PR:
1. 🟡 Unhandled promise rejection risk during gateway client teardown (possible process crash/DoS)
Description
Vulnerable code: void stopGatewayClient(client).finally(() => {
if (err) {
reject(err);
} else {
resolve(value as T);
}
});RecommendationEnsure teardown failures cannot surface as unhandled promise rejections. Option A (recommended): explicitly catch and swallow/log teardown errors: void stopGatewayClient(client)
.catch((e) => {
// optionally log debug-level; teardown errors should not crash the process
// logDebug(`stopGatewayClient failed: ${String(e)}`);
})
.finally(() => {
if (err) reject(err);
else resolve(value as T);
});Option B: make Analyzed PR: #70704 at commit Last updated on: 2026-04-23T17:29:32Z |
Greptile SummaryThis PR fixes a race condition in Confidence Score: 5/5This PR is safe to merge — it correctly resolves the race and the regression test clearly exercises the fixed path. The change is small and surgical. The stop() closure correctly sets settled = true and clears the timer synchronously before awaiting teardown, which is precisely the fix needed. Call sites that previously awaited teardown before calling stop are simplified correctly. The .finally() callback runs even if stopGatewayClient rejects, so resolve/reject are always reached. No P0 or P1 findings. No files require special attention. Reviews (1): Last reviewed commit: "fix(gateway): disarm wrapper timeout bef..." | Re-trigger Greptile |
The previous gateway teardown fix changed settlement ordering so one-shot CLI calls waited for
stopAndWait(), but it still left the wrapper timeout armed until teardown completed. That introduced a race: ifclient.request()succeeded or failed with a real RPC error neartimeoutMs, and teardown took slightly longer, the wrapper timer could fire first and replace the real outcome with a synthetic timeout.Example of the bad sequence:
This follow-up fixes the ordering by claiming the outcome and clearing the wrapper timeout before awaiting teardown, while still delaying final resolve/reject until teardown completes:
It also adds a regression test that blocks
stopAndWait(), advances fake timers pasttimeoutMs, and verifies a successful RPC still resolves with the real result instead of timing out.Verified:
pnpm test src/gateway/call.test.tscheck:changed, typechecks, lint, import-cycle guards, and gateway tests