Skip to content

Commit c2f28f8

Browse files
arul28claude
andcommitted
Don't Tear Down RPC Connection On Per-Call Timeout
A single slow call would call failConnection(), set closedError, and reject every subsequent call instantly with "Remote ADE service connection failed: timed out waiting for method ...". One stuck call on a heavy mutation (lane delete, sync) made every following folder open look like an 8s timeout even when the daemon answered in 5 ms. Per-call timeouts now reject only the one promise. The socket stays open and onDisconnect only fires on actual transport errors/close. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b7e6ab2 commit c2f28f8

2 files changed

Lines changed: 13 additions & 10 deletions

File tree

apps/desktop/src/main/services/remoteRuntime/runtimeRpcClient.test.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ describe("RuntimeRpcClient", () => {
104104
await expect(client.call("projects.list", {})).rejects.toThrow("broken pipe");
105105
});
106106

107-
it("fails the connection when a call times out", async () => {
107+
it("rejects only the timed-out call and keeps the connection open", async () => {
108108
vi.useFakeTimers();
109109
try {
110110
const transport = new MockTransport();
@@ -114,15 +114,17 @@ describe("RuntimeRpcClient", () => {
114114

115115
const pending = client.call("projects.list", {}, { timeoutMs: 25 });
116116
const assertion = expect(pending).rejects.toThrow(
117-
"Remote ADE service connection failed: timed out waiting for method projects.list.",
117+
"Remote ADE service timed out waiting for method projects.list (25ms).",
118118
);
119119
await vi.advanceTimersByTimeAsync(25);
120120

121121
await assertion;
122-
expect(onDisconnect).toHaveBeenCalledTimes(1);
123-
await expect(client.call("projects.list", {})).rejects.toThrow(
124-
"Remote ADE service connection failed: timed out waiting for method projects.list.",
125-
);
122+
expect(onDisconnect).not.toHaveBeenCalled();
123+
124+
// Subsequent calls go out fine — connection is still alive.
125+
const followup = client.call("projects.list", {});
126+
transport.emitData({ jsonrpc: "2.0", id: 2, result: { ok: true } });
127+
await expect(followup).resolves.toEqual({ ok: true });
126128
} finally {
127129
vi.useRealTimers();
128130
}

apps/desktop/src/main/services/remoteRuntime/runtimeRpcClient.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,11 @@ export class RuntimeRpcClient {
8181
}
8282
return new Promise((resolve, reject) => {
8383
const timer = setTimeout(() => {
84-
this.failConnection(
85-
new Error(
86-
`Remote ADE service connection failed: timed out waiting for method ${method}.`,
87-
),
84+
const pending = this.pending.get(id);
85+
if (!pending) return;
86+
this.pending.delete(id);
87+
pending.reject(
88+
new Error(`Remote ADE service timed out waiting for method ${method} (${timeoutMs}ms).`),
8889
);
8990
}, timeoutMs);
9091
this.pending.set(id, { resolve, reject, timer });

0 commit comments

Comments
 (0)