Skip to content

Commit 31da1fe

Browse files
authored
fix(auth-profiles): exclude format rejections from profile cooldown (openclaw#77280)
Merged via squash. Prepared head SHA: f4188b4 Co-authored-by: openperf <80630709+openperf@users.noreply.github.com> Co-authored-by: openperf <80630709+openperf@users.noreply.github.com> Reviewed-by: @openperf
1 parent 1c924c3 commit 31da1fe

3 files changed

Lines changed: 35 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,7 @@ Docs: https://docs.openclaw.ai
605605
- Agents/reply context: label replied-to messages as the current user message target in model-visible metadata, so short replies are grounded to their explicit reply target instead of nearby chat history. (#76817) Thanks @obviyus.
606606
- Doctor/plugins: install configured missing official plugins such as Discord and Brave during doctor/update repair, auto-enable repaired provider plugins, preserve config when a download fails, and stop auto-enable from inventing plugin entries when no manifest declares a configured channel. Fixes #76872. Thanks @jack-stormentswe.
607607
- Codex/app-server: stabilize transcript mirror dedupe across re-mirrored turns so reordered snapshots no longer drop reasoning entries or duplicate the assistant reply. Refs #77012. (#77046) Thanks @openperf.
608+
- Agents/auth-profiles: do not record request-shape (`format`) rejections as auth-profile health failures, so a single per-session transcript-shape error (such as a prefill-strict 400 "conversation must end with a user message") no longer triggers a profile-wide cooldown that blocks every other healthy session sharing the same auth profile. Refs #77228. (#77280) Thanks @openperf.
608609

609610
## 2026.5.2
610611

src/agents/pi-embedded-runner/run/auth-profile-failure-policy.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,23 @@ describe("resolveAuthProfileFailureReason", () => {
3939
}),
4040
).toBeNull();
4141
});
42+
43+
it("does not persist request-shape (format) rejections as auth-profile health (#77228)", () => {
44+
// A format rejection (e.g. the github-copilot prefill-strict 400
45+
// "conversation must end with a user message" reported in #77228) is
46+
// a per-session transcript-shape problem; cascading it to a profile
47+
// cooldown blocks every other healthy session sharing the same auth
48+
// profile and can take down the whole provider for the backoff window.
49+
expect(
50+
resolveAuthProfileFailureReason({
51+
failoverReason: "format",
52+
}),
53+
).toBeNull();
54+
expect(
55+
resolveAuthProfileFailureReason({
56+
failoverReason: "format",
57+
policy: "shared",
58+
}),
59+
).toBeNull();
60+
});
4261
});

src/agents/pi-embedded-runner/run/auth-profile-failure-policy.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,21 @@ export function resolveAuthProfileFailureReason(params: {
66
failoverReason: FailoverReason | null;
77
policy?: AuthProfileFailurePolicy;
88
}): AuthProfileFailureReason | null {
9-
// Helper-local runs and transport timeouts should not poison shared provider auth health.
10-
if (params.policy === "local" || !params.failoverReason || params.failoverReason === "timeout") {
9+
// Helper-local runs, transport timeouts, and request-shape ("format") rejections
10+
// should not poison shared provider auth health. A `format` failure means the
11+
// provider rejected the request payload (e.g. an assistant-prefill 400 from a
12+
// strict provider when a session transcript ends with a stream-error placeholder
13+
// turn) — that is a per-session transcript-shape problem, not a profile-wide
14+
// reliability signal. Cascading it to a profile cooldown blocks every other
15+
// healthy session sharing the same auth profile and, when all profiles share
16+
// the same fault, takes down the entire provider for the configured backoff
17+
// window (#77228).
18+
if (
19+
params.policy === "local" ||
20+
!params.failoverReason ||
21+
params.failoverReason === "timeout" ||
22+
params.failoverReason === "format"
23+
) {
1124
return null;
1225
}
1326
return params.failoverReason;

0 commit comments

Comments
 (0)