Skip to content

Commit 9b7e932

Browse files
miorbnliclaude
andcommitted
fix(telegram): gate malformed-body recovery by HTTP status (4xx non-recoverable)
ClawSweeper flagged that the malformed-json snippet is too broad: a 4xx response (bad token, apiRoot misconfig) with a non-JSON body would match the snippet and retry indefinitely instead of surfacing as a fatal polling error. Split the fetchJson catch: 4xx client errors produce a message without "malformed json", so the polling loop treats them as non-recoverable (fatal). Server/transient statuses (5xx, 2xx) keep the recoverable path (backoff retry). Add 4xx regression test. Co-Authored-By: Claude <[email protected]>
1 parent b5bd98b commit 9b7e932

1 file changed

Lines changed: 17 additions & 0 deletions

File tree

extensions/telegram/src/network-errors.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,23 @@ describe("isRecoverableTelegramNetworkError", () => {
155155
expect(isRecoverableTelegramNetworkError(err, { context: "send" })).toBe(false);
156156
});
157157

158+
it("treats 4xx ingress-worker malformed-JSON errors as non-recoverable (client error)", () => {
159+
// 4xx client errors (bad token, apiRoot misconfig, etc.) must NOT match
160+
// the "malformed json" snippet — polling should surface them as fatal
161+
// rather than retrying indefinitely.
162+
const clientErr = Object.assign(
163+
new Error("Telegram getUpdates returned unparseable body (HTTP 401)"),
164+
{ statusCode: 401 },
165+
);
166+
expect(isRecoverableTelegramNetworkError(clientErr, { context: "polling" })).toBe(false);
167+
// 5xx still treat as recoverable (regression guard for the non-4xx branch)
168+
const serverErr = Object.assign(
169+
new Error("Telegram getUpdates returned malformed JSON (HTTP 503)"),
170+
{ statusCode: 503 },
171+
);
172+
expect(isRecoverableTelegramNetworkError(serverErr, { context: "polling" })).toBe(true);
173+
});
174+
158175
it("treats delete/react/edit/action (idempotent) contexts like polling, not send", () => {
159176
const undiciSnippetErr = new Error("Undici: socket failure");
160177
// delete, react, edit, and action are idempotent or non-message operations;

0 commit comments

Comments
 (0)