mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-30 16:00:25 +00:00
fix: classify codex deactivated workspace codes
This commit is contained in:
@@ -51,6 +51,7 @@ Docs: https://docs.openclaw.ai
|
||||
- CLI/perf: serve `doctor`, `gateway`, `models`, and `plugins` parent help from startup metadata so common subcommand help avoids full CLI program construction. (#84786) Thanks @frankekn.
|
||||
- Codex/Lossless: keep context-engine history on the canonical run session when Telegram DMs use per-peer runtime policy keys. Fixes #84936. (#84954) Thanks @neeravmakwana.
|
||||
- Auth/OAuth: skip the refresh adapter when a stored OAuth credential has no refresh token so agent turns fail fast on missing-key instead of waiting on the 120s refresh timeout. Thanks @romneyda.
|
||||
- Codex/failover: classify `deactivated_workspace` as a permanent auth failure so configured fallback models can advance when a Codex workspace is deactivated. (#55893) Thanks @litang9.
|
||||
|
||||
## 2026.5.20
|
||||
|
||||
@@ -858,7 +859,6 @@ Docs: https://docs.openclaw.ai
|
||||
- CLI: route `plugins list --json` through the parsed command fast path and cover it in response budgets so plugin JSON inventory avoids full CLI registration work.
|
||||
- Control UI/Overview: render recent session rows through the shared session display resolver so label/displayName priority, key-equivalent labels, and channel fallbacks stay consistent with the chat selector. (#50696) Thanks @Maple778 and @BunsDev.
|
||||
- Gateway/network: keep OpenClaw-installed undici dispatchers on HTTP/1.1 and treat destroyed HTTP/2 session errors as recoverable network teardown, preventing `ERR_HTTP2_INVALID_SESSION` from crashing active gateway turns. Fixes #81627. (#81838) Thanks @joshavant.
|
||||
- Codex/failover: classify `deactivated_workspace` as a permanent auth failure so configured fallback models can advance when a Codex workspace is deactivated.
|
||||
- Memory/daily-files: widen the daily-memory file matcher used by Dreaming, rem-backfill, rem-harness, the doctor sweep, and short-term promotion so `memory/YYYY-MM-DD-<slug>.md` files written by the bundled session-memory hook (and any future slugged variants) are discovered alongside the date-only `memory/YYYY-MM-DD.md` shape. Date extraction still uses the leading `YYYY-MM-DD` capture group, so per-day ingestion/promotion semantics are unchanged for existing date-only files; slugged files now flow through the same paths instead of being silently skipped. Fixes #69536. Thanks @jack-stormentswe.
|
||||
- macOS/Gateway: fail managed LaunchAgent stop and restart when the configured gateway port remains busy after cleanup instead of reporting success while a listener survives. Fixes #73132. Thanks @BunsDev.
|
||||
- Telegram: reuse the sticky IPv4 Bot API transport for periodic getMe health checks, so IPv4-working hosts with broken IPv6 egress stop logging repeated probe timeouts. Fixes #76852. (#76856) Thanks @SymbolStar.
|
||||
|
||||
@@ -950,6 +950,28 @@ describe("failover-error", () => {
|
||||
expect(resolveFailoverReasonFromError({ message: "deactivated workspace" })).toBe(
|
||||
"auth_permanent",
|
||||
);
|
||||
expect(resolveFailoverReasonFromError({ code: "deactivated_workspace" })).toBe(
|
||||
"auth_permanent",
|
||||
);
|
||||
expect(
|
||||
resolveFailoverReasonFromError({
|
||||
detail: { code: "deactivated_workspace" },
|
||||
}),
|
||||
).toBe("auth_permanent");
|
||||
expect(
|
||||
resolveFailoverReasonFromError({
|
||||
status: 403,
|
||||
message: "Forbidden",
|
||||
detail: { code: "deactivated_workspace" },
|
||||
}),
|
||||
).toBe("auth_permanent");
|
||||
expect(
|
||||
resolveFailoverReasonFromError({
|
||||
status: 400,
|
||||
message: "Bad request",
|
||||
detail: { code: "deactivated_workspace" },
|
||||
}),
|
||||
).toBe("auth_permanent");
|
||||
});
|
||||
|
||||
it("403 OpenRouter 'Key limit exceeded' returns billing (model fallback trigger)", () => {
|
||||
|
||||
@@ -151,6 +151,11 @@ function readDirectErrorCode(err: unknown): string | undefined {
|
||||
const trimmed = directCode.trim();
|
||||
return trimmed ? trimmed : undefined;
|
||||
}
|
||||
const detailCode = (err as { detail?: { code?: unknown } }).detail?.code;
|
||||
if (typeof detailCode === "string") {
|
||||
const trimmed = detailCode.trim();
|
||||
return trimmed ? trimmed : undefined;
|
||||
}
|
||||
const status = (err as { status?: unknown }).status;
|
||||
if (typeof status !== "string" || /^\d+$/.test(status)) {
|
||||
return undefined;
|
||||
|
||||
@@ -758,6 +758,8 @@ function classifyFailoverReasonFromCode(raw: string | undefined): FailoverReason
|
||||
case "THROTTLINGEXCEPTION":
|
||||
case "THROTTLING_EXCEPTION":
|
||||
return "rate_limit";
|
||||
case "DEACTIVATED_WORKSPACE":
|
||||
return "auth_permanent";
|
||||
case "OVERLOADED":
|
||||
case "OVERLOADED_ERROR":
|
||||
return "overloaded";
|
||||
@@ -919,6 +921,10 @@ export function classifyFailoverSignal(signal: FailoverSignal): FailoverClassifi
|
||||
const messageClassification = signal.message
|
||||
? classifyFailoverClassificationFromMessage(signal.message, signal.provider)
|
||||
: null;
|
||||
const codeReason = classifyFailoverReasonFromCode(signal.code);
|
||||
if (codeReason === "auth_permanent") {
|
||||
return toReasonClassification(codeReason);
|
||||
}
|
||||
const statusClassification = classifyFailoverClassificationFromHttpStatus(
|
||||
inferredStatus,
|
||||
signal.message,
|
||||
@@ -929,7 +935,6 @@ export function classifyFailoverSignal(signal: FailoverSignal): FailoverClassifi
|
||||
if (statusClassification) {
|
||||
return statusClassification;
|
||||
}
|
||||
const codeReason = classifyFailoverReasonFromCode(signal.code);
|
||||
if (codeReason) {
|
||||
return toReasonClassification(codeReason);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user