You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`circuit_open: provider "${provider.name}" is in cooldown after ${AI_PROVIDER_FAILURE_THRESHOLD} consecutive failures — skipping this attempt`,
1264
+
circuit.structural
1265
+
? `circuit_open: provider "${provider.name}" has a structural config error (bad/missing credentials) — skipping until the cooldown expires; fix the underlying config, then restart`
1266
+
: `circuit_open: provider "${provider.name}" is in cooldown after ${AI_PROVIDER_FAILURE_THRESHOLD} consecutive failures — skipping this attempt`,
1254
1267
);
1255
1268
}
1256
1269
constrequestKindLabel=requestKind(options);
@@ -1290,9 +1303,16 @@ async function runProviderWithOtel(
1290
1303
// this catch runs, and computing `failures` from it would clobber a sibling call's write (lost-update race)
1291
1304
// instead of accumulating. No `await` between this read and the `.set()` below, so it's race-free.
expect(run).toHaveBeenCalledTimes(2);// 1 primary (rate-limited) + 1 fallback (succeeded on its first try).
2878
2879
});
2879
2880
2881
+
it("runDualAiTieBreakJudgeCall stops retrying a model after ONE structural codex-auth config error, same as a CLI timeout or 429 (GITTENSORY-K/8)",async()=>{
expect(primaryAttempts).toBe(1);// NOT 3 -- a structural config error is deterministic, so retrying is pointless.
3074
+
expect(run).toHaveBeenCalledTimes(2);// 1 primary (structural failure) + 1 fallback (succeeded on its first try).
3075
+
});
3076
+
3077
+
it("isStructuralProviderConfigError matches only codex's own structural-config error messages, not other Errors or non-Error throws (GITTENSORY-K/8)",()=>{
3078
+
expect(isStructuralProviderConfigError(newError("codex_auth_not_configured: ~/.codex/auth.json not found"))).toBe(true);
3079
+
expect(isStructuralProviderConfigError(newError("codex_no_auth: auth.json missing or expired"))).toBe(true);
Copy file name to clipboardExpand all lines: test/unit/selfhost-ai.test.ts
+57Lines changed: 57 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -735,6 +735,63 @@ describe("per-provider circuit breaker (#2540 — skip fast during a sustained o
735
735
// call exhausted the (single-provider) chain — a circuit-open throw still counts as a chain exhaustion.
736
736
expect(isAiProviderHealthy()).toBe(false);
737
737
});
738
+
739
+
it("opens the circuit on the very FIRST structural codex-auth failure, not after 3 (GITTENSORY-K/8 — a deterministic failure shouldn't pay for 3 real attempts)",async()=>{
740
+
constcalls=vi.fn(async()=>{
741
+
thrownewError("codex_auth_not_configured: ~/.codex/auth.json not found");
awaitexpect(createChainAi([provider]).run("m",{prompt: "x"})).rejects.toThrow(/connectionreset/);// 2nd failure, still below threshold 3 — reaches the real provider again
793
+
expect(calls).toHaveBeenCalledTimes(4);// structural-fail, succeed, transient-fail x2 — all reached the real provider.ai.run
0 commit comments