diff --git a/CHANGELOG.md b/CHANGELOG.md index 05b54daf3e7..80c96538260 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,16 +4,11 @@ Docs: https://docs.openclaw.ai ## Unreleased -### Fixes - -- Cron/failover: classify structured OpenAI-compatible `server_error` payloads as `server_error`, expose that reason in cron state, and let one-shot cron retry policy honor `retryOn: ["server_error"]` without requiring raw `5xx` text. (#45594) Thanks @clovericbot. - -## 2026.5.8 - ### Changes ### Fixes +- Cron/failover: classify structured OpenAI-compatible `server_error` payloads as `server_error`, expose that reason in cron state, and let one-shot cron retry policy honor `retryOn: ["server_error"]` without requiring raw `5xx` text. (#45594) Thanks @clovericbot. - Feishu: auto-thread `message(action="send")` replies inside the topic when the active session is group_topic or group_topic_sender, and propagate `replyInThread` through text, card, and media outbound adapters so topic-scoped sessions no longer post at the group root. Fixes #74903. (#77151) Thanks @ai-hpc. - WhatsApp: pass routing context into voice-note transcript echo preflight so echoed transcripts can deliver to the originating chat. Fixes #79778. (#79788) Thanks @hclsys. diff --git a/src/agents/failover-error.test.ts b/src/agents/failover-error.test.ts index 133e20952de..c853b329e06 100644 --- a/src/agents/failover-error.test.ts +++ b/src/agents/failover-error.test.ts @@ -986,9 +986,16 @@ describe("failover-error", () => { message: OPENAI_SERVER_ERROR_PAYLOAD, }), ).toBe("server_error"); + expect( + resolveFailoverReasonFromError({ + status: 500, + message: OPENAI_SERVER_ERROR_PAYLOAD, + }), + ).toBe("server_error"); const err = coerceToFailoverError( { + status: 500, message: OPENAI_SERVER_ERROR_PAYLOAD, }, { provider: "openai-codex", model: "gpt-5.4" }, diff --git a/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts b/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts index 6bcc719c6ad..ae8f5f6ad83 100644 --- a/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts +++ b/src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts @@ -683,6 +683,19 @@ describe("classifyFailoverReasonFromHttpStatus", () => { expect(classifyFailoverReasonFromHttpStatus(422, payload)).toBe("format"); }); + it("preserves structured server_error markers on explicit HTTP 5xx statuses", () => { + expect(classifyFailoverReasonFromHttpStatus(500, OPENAI_SERVER_ERROR_PAYLOAD)).toBe( + "server_error", + ); + expect(classifyFailoverReasonFromHttpStatus(502, OPENAI_SERVER_ERROR_PAYLOAD)).toBe( + "server_error", + ); + expect(classifyFailoverReasonFromHttpStatus(504, OPENAI_SERVER_ERROR_PAYLOAD)).toBe( + "server_error", + ); + expect(classifyFailoverReasonFromHttpStatus(500)).toBe("timeout"); + }); + it("treats generic HTTP 410 responses as retryable timeouts", () => { expect(classifyFailoverReasonFromHttpStatus(410)).toBe("timeout"); expect(classifyFailoverReasonFromHttpStatus(410, "")).toBe("timeout"); diff --git a/src/agents/pi-embedded-helpers/errors.ts b/src/agents/pi-embedded-helpers/errors.ts index 393e811186b..2a5de90000e 100644 --- a/src/agents/pi-embedded-helpers/errors.ts +++ b/src/agents/pi-embedded-helpers/errors.ts @@ -697,6 +697,9 @@ function classifyFailoverClassificationFromHttpStatus( return toReasonClassification("timeout"); } if (status === 500 || status === 502 || status === 504) { + if (messageReason === "server_error") { + return messageClassification; + } return toReasonClassification("timeout"); } if (status === 529) {