fix(auth): suppress recovery hint for format failures (#95779) · openclaw/openclaw@6db4624
Pick-cat
·
2026-06-27
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -23,12 +23,13 @@ const REASONS_WITH_RECOVERY: readonly FailoverReason[] = [
|
23 | 23 | "auth_permanent", |
24 | 24 | "billing", |
25 | 25 | ]; |
26 | | -const REASONS_TRANSIENT: readonly FailoverReason[] = [ |
| 26 | +const REASONS_WITHOUT_RECOVERY: readonly FailoverReason[] = [ |
27 | 27 | "rate_limit", |
28 | 28 | "overloaded", |
29 | 29 | "timeout", |
30 | 30 | "server_error", |
31 | 31 | "model_not_found", |
| 32 | +"format", |
32 | 33 | ]; |
33 | 34 | |
34 | 35 | describe("formatAuthProfileFailureMessage", () => { |
@@ -45,7 +46,7 @@ describe("formatAuthProfileFailureMessage", () => {
|
45 | 46 | }); |
46 | 47 | |
47 | 48 | it("omits the login command for transient cooldown reasons", () => { |
48 | | -for (const reason of REASONS_TRANSIENT) { |
| 49 | +for (const reason of REASONS_WITHOUT_RECOVERY) { |
49 | 50 | const message = formatAuthProfileFailureMessage({ |
50 | 51 | reason, |
51 | 52 | provider: PROVIDER, |
@@ -65,7 +66,11 @@ describe("formatAuthProfileFailureMessage", () => {
|
65 | 66 | }); |
66 | 67 | |
67 | 68 | it("always mentions the provider name", () => { |
68 | | -for (const reason of [...REASONS_WITH_RECOVERY, ...REASONS_TRANSIENT, "unknown"] as const) { |
| 69 | +for (const reason of [ |
| 70 | + ...REASONS_WITH_RECOVERY, |
| 71 | + ...REASONS_WITHOUT_RECOVERY, |
| 72 | +"unknown", |
| 73 | +] as const) { |
69 | 74 | const message = formatAuthProfileFailureMessage({ |
70 | 75 | reason, |
71 | 76 | provider: PROVIDER, |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -83,6 +83,7 @@ function shouldIncludeRecoveryHint(reason: FailoverReason): boolean {
|
83 | 83 | case "timeout": |
84 | 84 | case "server_error": |
85 | 85 | case "model_not_found": |
| 86 | +case "format": |
86 | 87 | return false; |
87 | 88 | default: |
88 | 89 | return true; |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -7127,6 +7127,28 @@ describe("runAgentTurnWithFallback", () => {
|
7127 | 7127 | } |
7128 | 7128 | }); |
7129 | 7129 | |
| 7130 | +it("does not suggest re-authentication for typed format failures", async () => { |
| 7131 | +state.runEmbeddedAgentMock.mockRejectedValueOnce( |
| 7132 | +new FailoverError("Format failover exhausted for provider openai", { |
| 7133 | +reason: "format", |
| 7134 | +provider: "openai", |
| 7135 | +authProfileFailure: { allInCooldown: true }, |
| 7136 | +cause: new Error("messages must alternate roles"), |
| 7137 | +}), |
| 7138 | +); |
| 7139 | + |
| 7140 | +const runAgentTurnWithFallback = await getRunAgentTurnWithFallback(); |
| 7141 | +const result = await runAgentTurnWithFallback(createMinimalRunAgentTurnParams()); |
| 7142 | + |
| 7143 | +expect(result.kind).toBe("final"); |
| 7144 | +if (result.kind === "final") { |
| 7145 | +expect(result.payload.text).toContain("Couldn't reach openai"); |
| 7146 | +expect(result.payload.text).toContain("messages must alternate roles"); |
| 7147 | +expect(result.payload.text).not.toContain("models auth login"); |
| 7148 | +expect(result.payload.text).not.toContain("openclaw configure"); |
| 7149 | +} |
| 7150 | +}); |
| 7151 | + |
7130 | 7152 | it("points stale openai missing-key failures at doctor repair with re-auth fallback", async () => { |
7131 | 7153 | state.runEmbeddedAgentMock.mockRejectedValueOnce( |
7132 | 7154 | new Error('No API key found for provider "openai".'), |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。