diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index dc29030434..f09a76f1ac 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -177,6 +177,26 @@ pub async fn run_main( } }; + // Fail fast if CODEX_HOME/rules is malformed (e.g. a file instead of a directory). + // This is a common misconfiguration and should not let the TUI start. + let rules_path = codex_home.join("rules"); + match std::fs::metadata(&rules_path) { + Ok(metadata) if !metadata.is_dir() => { + return Ok(AppExitInfo::fatal(format!( + "Failed to initialize codex: failed to load rules: failed to read rules files from {}: not a directory", + rules_path.display() + ))); + } + Ok(_) => {} + Err(err) if err.kind() == std::io::ErrorKind::NotFound => {} + Err(err) => { + return Ok(AppExitInfo::fatal(format!( + "Failed to initialize codex: failed to load rules: failed to read rules files from {}: {err}", + rules_path.display() + ))); + } + } + let cwd = cli.cwd.clone(); let config_cwd = match cwd.as_deref() { Some(path) => AbsolutePathBuf::from_absolute_path(path.canonicalize()?)?, diff --git a/codex-rs/tui/tests/suite/no_panic_on_startup.rs b/codex-rs/tui/tests/suite/no_panic_on_startup.rs index eade57d08e..ac9a9110a1 100644 --- a/codex-rs/tui/tests/suite/no_panic_on_startup.rs +++ b/codex-rs/tui/tests/suite/no_panic_on_startup.rs @@ -35,7 +35,10 @@ model_provider = "ollama" std::fs::write(codex_home.join("config.toml"), config_contents)?; let CodexCliOutput { exit_code, output } = run_codex_cli(codex_home, cwd).await?; - assert_ne!(0, exit_code, "Codex CLI should exit nonzero."); + assert_ne!( + 0, exit_code, + "Codex CLI should exit nonzero. Output was:\n{output}" + ); assert!( output.contains("ERROR: Failed to initialize codex:"), "expected startup error in output, got: {output}"