Skip to content

Commit 0d70c0b

Browse files
committed
fix(codex): surface app-server relogin details
1 parent 835e718 commit 0d70c0b

3 files changed

Lines changed: 64 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Docs: https://docs.openclaw.ai
1212
### Fixes
1313

1414
- iOS/chat: resize PhotosPicker image attachments to capped JPEGs before staging and sending, stripping source metadata and keeping oversized camera photos under the chat upload budget. Fixes #68524. Thanks @BunsDev.
15-
- Codex harness: classify native app-server token-refresh logout failures as authentication refresh errors, so users get re-authentication guidance instead of a raw runtime failure.
15+
- Codex harness: classify native app-server token-refresh logout and relogin failures as authentication refresh errors, so users get re-authentication guidance instead of a raw runtime failure.
1616
- Codex startup: treat selectable configured OpenAI agent models as Codex runtime requirements during plugin auto-enable, startup planning, and doctor install repair, so Anthropic-primary configs can still switch to OpenAI/Codex cleanly.
1717
- Agents: preserve source-reply delivery metadata when merging tool-returned media into the final reply, keeping message-tool-only replies deliverable and mirrored. Thanks @pashpashpash and @vincentkoc.
1818
- macOS/companion: require system TLS trust before pinning a first-use direct `wss://` gateway certificate and honor `gateway.remote.tlsFingerprint` as the explicit pin for remote node-mode sessions, so fresh endpoints fail closed when macOS cannot trust the certificate unless configured out of band. Fixes #50642. Thanks @BunsDev.

extensions/codex/src/app-server/client.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,42 @@ describe("CodexAppServerClient", () => {
127127
await expect(request).rejects.toHaveProperty("message", "Method not found");
128128
});
129129

130+
it("surfaces relogin details from Codex app-server RPC errors", async () => {
131+
const harness = createClientHarness();
132+
clients.push(harness.client);
133+
134+
const request = harness.client.request("thread/start", {});
135+
const outbound = JSON.parse(harness.writes[0] ?? "{}") as { id?: number };
136+
harness.send({
137+
id: outbound.id,
138+
error: {
139+
code: -32602,
140+
message: "failed to load configuration",
141+
data: {
142+
reason: "cloudRequirements",
143+
errorCode: "Auth",
144+
action: "relogin",
145+
statusCode: 401,
146+
detail:
147+
"Your authentication session could not be refreshed automatically. Please log out and sign in again.",
148+
},
149+
},
150+
});
151+
152+
await expect(request).rejects.toHaveProperty(
153+
"message",
154+
"failed to load configuration: Your authentication session could not be refreshed automatically. Please log out and sign in again.",
155+
);
156+
await expect(request).rejects.toHaveProperty("data", {
157+
reason: "cloudRequirements",
158+
errorCode: "Auth",
159+
action: "relogin",
160+
statusCode: 401,
161+
detail:
162+
"Your authentication session could not be refreshed automatically. Please log out and sign in again.",
163+
});
164+
});
165+
130166
it("rejects timed-out requests and ignores late responses", async () => {
131167
vi.useFakeTimers();
132168
const harness = createClientHarness();

extensions/codex/src/app-server/client.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,39 @@ export class CodexAppServerRpcError extends Error {
4242
readonly data?: JsonValue;
4343

4444
constructor(error: { code?: number; message: string; data?: JsonValue }, method: string) {
45-
super(error.message || `${method} failed`);
45+
super(formatCodexAppServerRpcErrorMessage(error, method));
4646
this.name = "CodexAppServerRpcError";
4747
this.code = error.code;
4848
this.data = error.data;
4949
}
5050
}
5151

52+
function formatCodexAppServerRpcErrorMessage(
53+
error: { message: string; data?: JsonValue },
54+
method: string,
55+
): string {
56+
const message = error.message || `${method} failed`;
57+
const detail = readCodexAppServerRpcReloginDetail(error.data);
58+
return detail && !message.includes(detail) ? `${message}: ${detail}` : message;
59+
}
60+
61+
function readCodexAppServerRpcReloginDetail(data: JsonValue | undefined): string | undefined {
62+
const record = isJsonObject(data) ? data : undefined;
63+
const nested = isJsonObject(record?.error) ? record.error : record;
64+
if (!nested) {
65+
return undefined;
66+
}
67+
const isRelogin =
68+
nested.action === "relogin" ||
69+
(nested.reason === "cloudRequirements" && nested.errorCode === "Auth");
70+
const detail = typeof nested.detail === "string" ? nested.detail.trim() : "";
71+
return isRelogin && detail ? detail : undefined;
72+
}
73+
74+
function isJsonObject(value: unknown): value is { [key: string]: JsonValue } {
75+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
76+
}
77+
5278
export function isCodexAppServerConnectionClosedError(error: unknown): boolean {
5379
if (!(error instanceof Error)) {
5480
return false;

0 commit comments

Comments
 (0)