From c9dffe9b5ce1c0bee0997b26b7bb82e71f55d1f6 Mon Sep 17 00:00:00 2001 From: Alexandr Suhinin Date: Tue, 21 Jul 2026 13:37:19 +0300 Subject: [PATCH] misc: append workaround clarification for reload configuration errors --- src/CodexAcpClient.ts | 8 +++++++- src/CodexAcpServer.ts | 4 ++++ .../CodexACPAgent/mcp-config-merge.test.ts | 4 +++- .../CodexACPAgent/new-session-logout.test.ts | 18 ++++++++++++++++++ 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/CodexAcpClient.ts b/src/CodexAcpClient.ts index 3c48232c..4961eb37 100644 --- a/src/CodexAcpClient.ts +++ b/src/CodexAcpClient.ts @@ -72,6 +72,7 @@ export class CodexAcpClient { private pendingAccountUpdated: Promise | null = null; private readonly sessionNotificationQueues = new Map>(); private skillExtraRoots: string[] = []; + private configPath: string | null = null; constructor(codexClient: CodexAppServerClient, codexConfig?: JsonObject, modelProvider?: string) { @@ -86,7 +87,7 @@ export class CodexAcpClient { }; async initialize(request: acp.InitializeRequest): Promise { - await this.codexClient.initialize({ + const response = await this.codexClient.initialize({ capabilities: { experimentalApi: true, requestAttestation: false, @@ -97,6 +98,11 @@ export class CodexAcpClient { title: request.clientInfo?.title ?? this.defaultClientInfo.title, } }); + this.configPath = response?.codexHome ?? null; + } + + getHomePath(): string | null { + return this.configPath; } async authenticate(authRequest: acp.AuthenticateRequest): Promise { diff --git a/src/CodexAcpServer.ts b/src/CodexAcpServer.ts index 24a883e7..447721c9 100644 --- a/src/CodexAcpServer.ts +++ b/src/CodexAcpServer.ts @@ -310,6 +310,10 @@ export class CodexAcpServer { await this.refreshSessionsAuthState(null); throw RequestError.internalError(`${(e.message)}\n\nYou have been logged out. Please try again.`); } + const configPath = this.codexAcpClient.getHomePath() ?? "global"; + if (e.message.includes("load config")) { + throw RequestError.internalError(`${e.message}\n\nCheck ${configPath} and project .codex directories, especially their config.toml files, or any CODEX_CONFIG override.`); + } } private beginSessionOpen(sessionId: string): number { diff --git a/src/__tests__/CodexACPAgent/mcp-config-merge.test.ts b/src/__tests__/CodexACPAgent/mcp-config-merge.test.ts index ac387e5c..94a92d71 100644 --- a/src/__tests__/CodexACPAgent/mcp-config-merge.test.ts +++ b/src/__tests__/CodexACPAgent/mcp-config-merge.test.ts @@ -114,6 +114,8 @@ url = "https://example.com/mcp" await expect(codexAcpAgent.newSession({ cwd: "", mcpServers: [conflictingMcp], - })).rejects.toThrow("url is not supported for stdio"); + })).rejects.toMatchObject({ + data: expect.stringContaining("url is not supported for stdio"), + }); }); }); diff --git a/src/__tests__/CodexACPAgent/new-session-logout.test.ts b/src/__tests__/CodexACPAgent/new-session-logout.test.ts index f2f331eb..01a7b429 100644 --- a/src/__tests__/CodexACPAgent/new-session-logout.test.ts +++ b/src/__tests__/CodexACPAgent/new-session-logout.test.ts @@ -41,6 +41,24 @@ describe("New session logout handling", () => { expect(logoutSpy).toHaveBeenCalledOnce(); }); + it("includes the global config path in reload configuration errors", async () => { + const fixture = createCodexMockTestFixture(); + const codexAcpAgent = fixture.getCodexAcpAgent(); + const codexAcpClient = fixture.getCodexAcpClient(); + const codexAppServerClient = fixture.getCodexAppServerClient(); + vi.spyOn(codexAcpClient, "authRequired").mockResolvedValue(false); + const logoutSpy = vi.spyOn(codexAcpClient, "logout").mockResolvedValue(); + + const errorMessage = 'Internal error: "failed to reload config: filesystem path `/tmp` must be absolute, use `~/...`, or start with `:`"'; + vi.spyOn(codexAppServerClient, "threadStart").mockRejectedValue(new Error(errorMessage)); + + expect(logoutSpy).toHaveBeenCalledTimes(0); + await expect(codexAcpAgent.newSession({cwd: "", mcpServers: []})) + .rejects.toMatchObject({ + data: expect.stringContaining(`Check global and project .codex directories`), + }); + }); + it("refreshes OpenAI sessions when newSession error forces logout", async () => { const fixture = createCodexMockTestFixture(); const codexAcpAgent = fixture.getCodexAcpAgent();