From c84721c0905bed335e73587bd0a1e76afef3bb3b Mon Sep 17 00:00:00 2001 From: luvs01 Date: Tue, 11 Aug 2026 09:28:22 +0900 Subject: [PATCH] fix(doctor): tolerate malformed Codex agents path --- src/cli/doctor.ts | 12 ++++++++++-- src/codex/subagent-model-fallback.ts | 12 ++++++++++-- tests/subagent-model-fallback.test.ts | 12 ++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/cli/doctor.ts b/src/cli/doctor.ts index 64d62ce8aa..686e287e84 100644 --- a/src/cli/doctor.ts +++ b/src/cli/doctor.ts @@ -944,8 +944,16 @@ export async function runDoctor(args: string[] = []): Promise { } console.log("\nCodex agent role files"); - const tomlFallbackRoles = scanCodexAgentRolesWithTomlModelFallback(resolveCodexHomeDirImpl()); - if (tomlFallbackRoles.length === 0) { + let roleScanError: unknown; + const tomlFallbackRoles = scanCodexAgentRolesWithTomlModelFallback( + resolveCodexHomeDirImpl(), + cause => { + roleScanError = cause; + }, + ); + if (roleScanError) { + console.log(` [WARN] unable to scan $CODEX_HOME/agents/*.toml: ${String(roleScanError)}`); + } else if (tomlFallbackRoles.length === 0) { console.log(" ok no per-role model_fallback fields in $CODEX_HOME/agents/*.toml"); } else { console.log(` [WARN] ${tomlFallbackRoles.length} agent role file${tomlFallbackRoles.length === 1 ? "" : "s"} contain${tomlFallbackRoles.length === 1 ? "s" : ""} \`model_fallback\`: ${tomlFallbackRoles.join(", ")}`); diff --git a/src/codex/subagent-model-fallback.ts b/src/codex/subagent-model-fallback.ts index da40ebffff..fd8a3ec51d 100644 --- a/src/codex/subagent-model-fallback.ts +++ b/src/codex/subagent-model-fallback.ts @@ -765,8 +765,16 @@ export function hasCodexAgentModelFallbackField(role: string, codexHome = CODEX_ } /** Roles whose TOML still carries `model_fallback`, including empty arrays. */ -export function scanCodexAgentRolesWithTomlModelFallback(codexHome = CODEX_HOME): string[] { - return listCodexAgentRoles(codexHome).filter(role => hasCodexAgentModelFallbackField(role, codexHome)); +export function scanCodexAgentRolesWithTomlModelFallback( + codexHome = CODEX_HOME, + onListError?: (cause: unknown) => void, +): string[] { + try { + return listCodexAgentRoles(codexHome).filter(role => hasCodexAgentModelFallbackField(role, codexHome)); + } catch (cause) { + onListError?.(cause); + return []; + } } export function listCodexAgentRoles(codexHome = CODEX_HOME): string[] { diff --git a/tests/subagent-model-fallback.test.ts b/tests/subagent-model-fallback.test.ts index 3d433aef12..e7d8d6e577 100644 --- a/tests/subagent-model-fallback.test.ts +++ b/tests/subagent-model-fallback.test.ts @@ -1054,6 +1054,18 @@ describe("subagent model fallback chain", () => { expect(readCodexAgentModelFallback("empty_fallback", dir)).toEqual([]); }); + test("scanCodexAgentRolesWithTomlModelFallback tolerates a malformed agents path", () => { + const dir = codexHomeFixture(); + rmSync(join(dir, "agents"), { recursive: true }); + writeFileSync(join(dir, "agents"), "not a directory", "utf8"); + let scanError: unknown; + + expect(scanCodexAgentRolesWithTomlModelFallback(dir, cause => { + scanError = cause; + })).toEqual([]); + expect(scanError).toBeInstanceOf(Error); + }); + test("scanCodexAgentRolesWithTomlModelFallback recognizes quoted model_fallback keys", () => { const dir = codexHomeFixture(); writeFileSync(join(dir, "agents", "quoted_key.toml"), [