Skip to content

Commit d3ea0d7

Browse files
fix: remap grpc "client error (Connect)" to UnavailableError for reconnection (#461)
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. Co-authored-by: William Chong <william-chong@outlook.com>
1 parent 39fb786 commit d3ea0d7

File tree

2 files changed

+10
-4
lines changed

2 files changed

+10
-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: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,7 @@ describe.skip("reconnect", () => {
9494
})) {
9595
count++;
9696
}
97-
}).rejects.toThrowErrorMatchingInlineSnapshot(
98-
`"UnknownError("Server-side error: status: Unknown, message: \\"client error (Connect)\\", details: [], metadata: MetadataMap { headers: {} }")"`
99-
);
97+
}).rejects.toThrowError(UnavailableError);
10098
// create subsctiption
10199
await expect(
102100
client.createPersistentSubscriptionToStream(
@@ -127,6 +125,7 @@ describe.skip("reconnect", () => {
127125
'"Failed to discover after 10 attempts."'
128126
);
129127
// read the stream
128+
// Channel is torn down by now, so readStream goes through discovery which fails.
130129
await expect(async () => {
131130
let count = 0;
132131
for await (const e of client.readStream(STREAM_NAME, {
@@ -135,7 +134,7 @@ describe.skip("reconnect", () => {
135134
count++;
136135
}
137136
}).rejects.toThrowErrorMatchingInlineSnapshot(
138-
`"UnknownError("Server-side error: status: Unknown, message: \\"client error (Connect)\\", details: [], metadata: MetadataMap { headers: {} }")"`
137+
'"Failed to discover after 10 attempts."'
139138
);
140139
// create subsctiption
141140
await expect(

0 commit comments

Comments
 (0)