Skip to content

fix: report an appropriate error in the TUI for malformed rules#9011

Merged
bolinfest merged 1 commit intomainfrom
pr9011
Jan 13, 2026
Merged

fix: report an appropriate error in the TUI for malformed rules#9011
bolinfest merged 1 commit intomainfrom
pr9011

Conversation

@bolinfest
Copy link
Collaborator

@bolinfest bolinfest commented Jan 10, 2026

The underlying issue is that when we encountered an error starting a conversation (any sort of error, though making $CODEX_HOME/rules a file rather than folder was the example in #8803), then we were writing the message to stderr, but this could be printed over by our UI framework so the user would not see it. In general, we disallow the use of eprintln!() in this part of the code for exactly this reason, though this was suppressed by an #[allow(clippy::print_stderr)].

This attempts to clean things up by changing handle_event() and handle_tui_event() to return a Result<AppRunControl> instead of a Result<bool>, which is a new type introduced in this PR (and depends on ExitReason, also a new type):

#[derive(Debug)]
pub(crate) enum AppRunControl {
    Continue,
    Exit(ExitReason),
}

#[derive(Debug, Clone)]
pub enum ExitReason {
    UserRequested,
    Fatal(String),
}

This makes it possible to exit the primary control flow of the TUI with richer information. This PR adds ExitReason to the existing AppExitInfo struct and updates handle_app_exit() to print the error and exit code 1 in the event of ExitReason::Fatal.

I tried to create an integration test for this, but it was a bit involved, so I published it as a separate PR: #9166. For this PR, please have faith in my manual testing!

Fixes #8803.


Stack created with Sapling. Best reviewed with ReviewStack.

@etraut-openai
Copy link
Collaborator

Will this also work for api service clients like the IDE extension? Looks like it's TUI-only?

@@ -30,16 +30,14 @@ pub(crate) fn spawn_agent(
..
} = match server.start_thread(config).await {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@etraut-openai to answer your question about "Will this also work for api service clients like the IDE extension?" I believe the crux of the issue is how the UI deals with an Err from start_thread().

I believe it is already doing the right thing here:

match self.thread_manager.start_thread(config).await {
Ok(new_thread) => {
let NewThread {
thread_id,
session_configured,
..
} = new_thread;
let response = NewConversationResponse {
conversation_id: thread_id,
model: session_configured.model,
reasoning_effort: session_configured.reasoning_effort,
rollout_path: session_configured.rollout_path,
};
self.outgoing.send_response(request_id, response).await;
}
Err(err) => {
let error = JSONRPCErrorError {
code: INTERNAL_ERROR_CODE,
message: format!("error creating conversation: {err}"),
data: None,
};
self.outgoing.send_error(request_id, error).await;
}
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm working on a new PR that surfaces config parsing errors correctly to app server clients. I'll extend it to handle malformed rules errors as well.

bolinfest added a commit that referenced this pull request Jan 13, 2026
bolinfest added a commit that referenced this pull request Jan 13, 2026

/// Handle the app exit and print the results. Optionally run the update action.
fn handle_app_exit(exit_info: AppExitInfo) -> anyhow::Result<()> {
match exit_info.exit_reason {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this runs only in interactive mode. What about non-interactive mode (codex exec)? I think we'd want this same error printed in that case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This already appears to work as intended for codex exec:

$ ./codex-rs/target/debug/codex exec hi
Error: Fatal error: failed to load execpolicy: failed to read execpolicy files from /Users/mbolin/.codex/rules: Not a directory (os error 20)

@@ -30,16 +30,14 @@ pub(crate) fn spawn_agent(
..
} = match server.start_thread(config).await {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm working on a new PR that surfaces config parsing errors correctly to app server clients. I'll extend it to handle malformed rules errors as well.

token_usage: codex_core::protocol::TokenUsage::default(),
thread_id: None,
update_action: Some(action),
fatal_error_message: ExitReason::UserRequested,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fatal_error_message: ExitReason::UserRequested,
exit_reason: ExitReason::UserRequested,

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Fixed and ran cargo build --bin codex --release locally to verify the fix.

Copy link
Collaborator

@joshka-oai joshka-oai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved - but make sure to fix #9011 (comment)

bolinfest added a commit that referenced this pull request Jan 13, 2026
@bolinfest bolinfest enabled auto-merge (squash) January 13, 2026 22:58
@bolinfest bolinfest merged commit 2cd1a0a into main Jan 13, 2026
81 of 85 checks passed
@bolinfest bolinfest deleted the pr9011 branch January 13, 2026 23:21
@github-actions github-actions bot locked and limited conversation to collaborators Jan 13, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

v0.78.0 panics instead of graceful error when .codex/rules contains invalid Starlark

3 participants