fix(rpc): bound retryable status range and add malformed-response code

- isRetryableStatus now fails closed for out-of-range values (600+, negative,
  NaN) by bounding the 5xx check on both sides (>= 500 && <= 599), instead of
  an unbounded >= 500 that classified garbage statuses like 1000 as retryable.
- 408 Request Timeout is now retryable, matching the RPC_TRANSPORT doc
  comment (connection, timeout, 5xx) — a timeout surfaced as 408 is no longer
  treated differently from the same timeout surfaced as 504.
- Add RPC_MALFORMED: the callee answered, but not with a ServiceResult (HTML
  error page, truncated body, unexpected shape). Distinct from RPC_TRANSPORT
  since something DID respond; non-retryable via the existing retryableFor,
  no new branch needed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-05 13:45:37 +05:30
co-authored by Claude Opus 5
parent dd8354477d
commit 21ea8a84a0
2 changed files with 33 additions and 3 deletions
+18
View File
@@ -23,12 +23,30 @@ describe("rpc errors", () => {
expect(isRetryableStatus(500)).toBe(true);
expect(isRetryableStatus(503)).toBe(true);
expect(isRetryableStatus(429)).toBe(true);
expect(isRetryableStatus(599)).toBe(true);
expect(isRetryableStatus(408)).toBe(true);
expect(isRetryableStatus(400)).toBe(false);
expect(isRetryableStatus(403)).toBe(false);
expect(isRetryableStatus(404)).toBe(false);
expect(isRetryableStatus(409)).toBe(false);
expect(isRetryableStatus(200)).toBe(false);
});
test("an out-of-range status fails closed", () => {
for (const status of [600, 1000, 0, -1, Number.NaN]) {
expect(isRetryableStatus(status)).toBe(false);
}
});
test("a malformed response is never retryable", () => {
const f = failure(RPC_ERROR_CODES.malformed, "Unexpected response shape");
expect(f.ok).toBe(false);
if (!f.ok) {
expect(f.code).toBe("RPC_MALFORMED");
expect(f.retryable).toBe(false);
}
});
test("a denial is never retryable", () => {
const f = failure(RPC_ERROR_CODES.denied, "Forbidden");
if (!f.ok) expect(f.retryable).toBe(false);