Skip to content

Commit ac736d7

Browse files
committed
Clarify TUI thread goal errors
1 parent 5966a95 commit ac736d7

2 files changed

Lines changed: 55 additions & 9 deletions

File tree

codex-rs/tui/src/app/thread_goal_actions.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::goal_display::goal_usage_summary;
1111
use codex_app_server_protocol::ThreadGoal;
1212
use codex_app_server_protocol::ThreadGoalStatus;
1313
use codex_protocol::ThreadId;
14+
use color_eyre::Report;
1415

1516
const EPHEMERAL_THREAD_GOAL_ERROR_MESSAGE: &str = concat!(
1617
"Goals need a saved session. This session is temporary.\n",
@@ -61,7 +62,11 @@ impl App {
6162
let response = match result {
6263
Ok(response) => response,
6364
Err(err) => {
64-
tracing::warn!("failed to read thread goal after resume: {err}");
65+
if is_ephemeral_thread_goal_error(&err) {
66+
tracing::debug!("skipping resume goal prompt for ephemeral thread: {err}");
67+
} else {
68+
tracing::warn!("failed to read thread goal after resume: {err}");
69+
}
6570
return;
6671
}
6772
};
@@ -286,15 +291,17 @@ impl App {
286291
}
287292
}
288293

289-
fn thread_goal_error_message(action: &str, err: &color_eyre::Report) -> String {
294+
fn thread_goal_error_message(action: &str, err: &Report) -> String {
290295
if is_ephemeral_thread_goal_error(err) {
291-
EPHEMERAL_THREAD_GOAL_ERROR_MESSAGE.to_string()
292-
} else {
293-
format!("Failed to {action} thread goal: {err}")
296+
return EPHEMERAL_THREAD_GOAL_ERROR_MESSAGE.to_string();
297+
}
298+
if err.to_string().contains("goals feature is disabled") {
299+
return "Goals are not enabled for this session.".to_string();
294300
}
301+
format!("Failed to {action} thread goal: {err}")
295302
}
296303

297-
fn is_ephemeral_thread_goal_error(err: &color_eyre::Report) -> bool {
304+
fn is_ephemeral_thread_goal_error(err: &Report) -> bool {
298305
err.chain().any(|cause| {
299306
let message = cause.to_string();
300307
message.contains("ephemeral thread does not support goals")

codex-rs/tui/src/app_server_session.rs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,15 @@ fn is_thread_settings_update_unsupported(source: &JSONRPCErrorError) -> bool {
139139
&& source.message.contains(THREAD_SETTINGS_UPDATE_METHOD))
140140
}
141141

142+
fn thread_goal_request_error(context: &'static str, err: TypedRequestError) -> color_eyre::Report {
143+
match err {
144+
TypedRequestError::Server { source, .. } => {
145+
color_eyre::eyre::eyre!("{}", source.message)
146+
}
147+
err => color_eyre::eyre::eyre!("{context}: {err}"),
148+
}
149+
}
150+
142151
/// Data collected during the TUI bootstrap phase that the main event loop
143152
/// needs to configure the UI, telemetry, and initial rate-limit prefetch.
144153
///
@@ -798,7 +807,7 @@ impl AppServerSession {
798807
},
799808
})
800809
.await
801-
.wrap_err("thread/goal/get failed in TUI")
810+
.map_err(|err| thread_goal_request_error("thread/goal/get failed in TUI", err))
802811
}
803812

804813
pub(crate) async fn thread_goal_set(
@@ -820,7 +829,7 @@ impl AppServerSession {
820829
},
821830
})
822831
.await
823-
.wrap_err("thread/goal/set failed in TUI")
832+
.map_err(|err| thread_goal_request_error("thread/goal/set failed in TUI", err))
824833
}
825834

826835
pub(crate) async fn thread_goal_clear(
@@ -836,7 +845,7 @@ impl AppServerSession {
836845
},
837846
})
838847
.await
839-
.wrap_err("thread/goal/clear failed in TUI")
848+
.map_err(|err| thread_goal_request_error("thread/goal/clear failed in TUI", err))
840849
}
841850

842851
pub(crate) async fn logout_account(&mut self) -> Result<()> {
@@ -1740,6 +1749,36 @@ mod tests {
17401749
use pretty_assertions::assert_eq;
17411750
use tempfile::TempDir;
17421751

1752+
#[test]
1753+
fn thread_goal_request_error_surfaces_server_message() {
1754+
let error = TypedRequestError::Server {
1755+
method: "thread/goal/get".to_string(),
1756+
source: JSONRPCErrorError {
1757+
code: -32600,
1758+
message: "ephemeral thread does not support goals: thr_123".to_string(),
1759+
data: None,
1760+
},
1761+
};
1762+
1763+
assert_eq!(
1764+
thread_goal_request_error("thread/goal/get failed in TUI", error).to_string(),
1765+
"ephemeral thread does not support goals: thr_123"
1766+
);
1767+
}
1768+
1769+
#[test]
1770+
fn thread_goal_request_error_keeps_transport_context() {
1771+
let error = TypedRequestError::Transport {
1772+
method: "thread/goal/get".to_string(),
1773+
source: std::io::Error::new(std::io::ErrorKind::BrokenPipe, "broken pipe"),
1774+
};
1775+
1776+
assert_eq!(
1777+
thread_goal_request_error("thread/goal/get failed in TUI", error).to_string(),
1778+
"thread/goal/get failed in TUI: thread/goal/get transport error: broken pipe"
1779+
);
1780+
}
1781+
17431782
async fn build_config(temp_dir: &TempDir) -> Config {
17441783
ConfigBuilder::default()
17451784
.codex_home(temp_dir.path().to_path_buf())

0 commit comments

Comments
 (0)