From f7468bb5dabbe2c1a4b5a4100914e6bd25c035c9 Mon Sep 17 00:00:00 2001 From: Yoon Park Date: Mon, 10 Aug 2026 09:33:19 -0700 Subject: [PATCH] fix(sdk): suppress unhandledRejection in PendingRequestMap.create() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Callers (sendControlRequest, request) store the promise then suspend on await transport.write() before returning it. If the read loop rejects the promise during that window, Node fires unhandledRejection and terminates the process because no handler is attached yet. Adding a noop .catch() marks the promise as handled without swallowing the error — we return p, not p.catch()'s result, so the rejection still propagates to the caller normally. Fixes ClaudeAxonConnection and CodexAxonConnection. Co-Authored-By: Claude Sonnet 4.6 --- sdk/src/shared/pending-request-map.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sdk/src/shared/pending-request-map.ts b/sdk/src/shared/pending-request-map.ts index a5b908e..1e18a23 100644 --- a/sdk/src/shared/pending-request-map.ts +++ b/sdk/src/shared/pending-request-map.ts @@ -13,12 +13,21 @@ export class PendingRequestMap { // Silently overwriting would orphan the old entry's promise and leave its // timer racing against the new entry's — surface the caller bug instead. if (this.pending.has(id)) throw new Error(`Duplicate pending request id: ${String(id)}`); - return new Promise((resolve, reject) => { + const p = new Promise((resolve, reject) => { const timer = setTimeout(() => { if (this.pending.delete(id)) reject(new Error(timeoutMessage)); }, timeoutMs); this.pending.set(id, { resolve, reject, timer }); }); + // Suppress unhandledRejection during the window between create() and the + // caller awaiting the returned promise. Callers store the promise, then + // suspend on an await (e.g. transport.write) before returning it. If the + // read loop rejects this promise during that suspension, Node fires + // unhandledRejection and terminates the process. The noop .catch() marks + // the promise as handled without swallowing the error — we return p, not + // p.catch()'s result, so the rejection still propagates to the caller. + p.catch(() => {}); + return p; } resolve(id: Id, value: Value): boolean {