Skip to content

Commit 02c24dd

Browse files
committed
fix: remap grpc "client error (Connect)" to UnavailableError for reconnection
grpc-js reports transport-level connection failures on streaming RPCs as StatusCode.Unknown instead of StatusCode.Unavailable. This prevents the client from tearing down the dead channel and triggering cluster rediscovery when the connected node goes down. The fix remaps errors containing "client error (Connect)" to UnavailableError in convertToCommandError, consistent with the existing "write after end" workaround. This ensures shouldReconnect returns true, the channel is torn down, and discovery finds the new leader.
1 parent efd197f commit 02c24dd

File tree

2 files changed

+12
-4
lines changed

2 files changed

+12
-4
lines changed

packages/db-client/src/utils/CommandError.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,13 @@ export const convertToCommandError = (error: Error): CommandError | Error => {
652652
return new UnavailableError(error);
653653
}
654654

655+
// grpc-js reports transport-level connection failures on streaming RPCs as
656+
// StatusCode.Unknown instead of StatusCode.Unavailable. This prevents the
657+
// client from tearing down the dead channel and triggering rediscovery.
658+
if (error.details.includes("client error (Connect)")) {
659+
return new UnavailableError(error);
660+
}
661+
655662
return new UnknownError(error);
656663
};
657664

packages/test/src/connection/reconnect/all-nodes-down.test.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,16 @@ describe.skip("reconnect", () => {
8787
)
8888
).rejects.toThrowError(UnavailableError);
8989
// read the stream
90+
// With the fix, "client error (Connect)" is now correctly classified as UnavailableError
91+
// instead of UnknownError, which triggers reconnection (channel teardown + rediscovery).
9092
await expect(async () => {
9193
let count = 0;
9294
for await (const e of client.readStream(STREAM_NAME, {
9395
maxCount: 10,
9496
})) {
9597
count++;
9698
}
97-
}).rejects.toThrowErrorMatchingInlineSnapshot(
98-
`"UnknownError("Server-side error: status: Unknown, message: \\"client error (Connect)\\", details: [], metadata: MetadataMap { headers: {} }")"`
99-
);
99+
}).rejects.toThrowError(UnavailableError);
100100
// create subsctiption
101101
await expect(
102102
client.createPersistentSubscriptionToStream(
@@ -127,6 +127,7 @@ describe.skip("reconnect", () => {
127127
'"Failed to discover after 10 attempts."'
128128
);
129129
// read the stream
130+
// Channel is torn down by now, so readStream goes through discovery which fails.
130131
await expect(async () => {
131132
let count = 0;
132133
for await (const e of client.readStream(STREAM_NAME, {
@@ -135,7 +136,7 @@ describe.skip("reconnect", () => {
135136
count++;
136137
}
137138
}).rejects.toThrowErrorMatchingInlineSnapshot(
138-
`"UnknownError("Server-side error: status: Unknown, message: \\"client error (Connect)\\", details: [], metadata: MetadataMap { headers: {} }")"`
139+
'"Failed to discover after 10 attempts."'
139140
);
140141
// create subsctiption
141142
await expect(

0 commit comments

Comments
 (0)