From 9df71691d2dbff440141b9e9006c664b7ead0a00 Mon Sep 17 00:00:00 2001 From: Charles Cunningham Date: Mon, 16 Feb 2026 23:35:16 -0800 Subject: [PATCH] Resolve session storage URL async with timeout --- codex-rs/core/src/config/mod.rs | 32 +++++++++++++++++++++++++----- codex-rs/tui/src/chatwidget.rs | 35 ++++++++++++++++++--------------- codex-rs/tui/src/lib.rs | 2 +- 3 files changed, 47 insertions(+), 22 deletions(-) diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index 6bf4f09f1e..21912f4205 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -79,6 +79,7 @@ use std::io::ErrorKind; use std::path::Path; use std::path::PathBuf; use std::process::Command; +use std::time::Duration; #[cfg(test)] use tempfile::tempdir; #[cfg(not(target_os = "macos"))] @@ -110,6 +111,7 @@ pub use codex_git::GhostSnapshotConfig; /// the context window. pub(crate) const PROJECT_DOC_MAX_BYTES: usize = 32 * 1024; // 32 KiB pub(crate) const DEFAULT_AGENT_MAX_THREADS: Option = Some(6); +const SESSION_OBJECT_STORAGE_URL_CMD_TIMEOUT: Duration = Duration::from_secs(5); pub const CONFIG_TOML_FILE: &str = "config.toml"; @@ -1904,15 +1906,35 @@ impl Config { Ok(config) } - pub fn resolve_session_object_storage_url(&self) -> std::io::Result> { + pub async fn resolve_session_object_storage_url(&self) -> std::io::Result> { if let Some(url) = self.session_object_storage_url.as_ref() { return Ok(Some(url.clone())); } - Self::try_run_command_for_value( - self.session_object_storage_url_cmd.as_deref(), - "session_object_storage_url_cmd", - ) + let Some(command) = self.session_object_storage_url_cmd.clone() else { + return Ok(None); + }; + + let mut handle = tokio::task::spawn_blocking(move || { + Self::try_run_command_for_value(Some(&command), "session_object_storage_url_cmd") + }); + let resolved = + match tokio::time::timeout(SESSION_OBJECT_STORAGE_URL_CMD_TIMEOUT, &mut handle).await { + Ok(result) => result.map_err(|err| { + std::io::Error::other(format!("session_object_storage_url_cmd panicked: {err}")) + })?, + Err(_) => { + handle.abort(); + return Err(std::io::Error::new( + ErrorKind::TimedOut, + format!( + "session_object_storage_url_cmd timed out after {}s", + SESSION_OBJECT_STORAGE_URL_CMD_TIMEOUT.as_secs() + ), + )); + } + }; + Ok(resolved?) } fn load_instructions(codex_dir: Option<&Path>) -> Option { diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 89d08eaf1c..cbc8a80baa 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -3804,22 +3804,6 @@ impl ChatWidget { // TODO: Enforce email-based access once we have a server-side auth check. - let storage_url = match self.config.resolve_session_object_storage_url() { - Ok(Some(url)) => url, - Ok(None) => { - self.add_error_message( - "Sharing requires `session_object_storage_url` or `session_object_storage_url_cmd` in config.toml." - .to_string(), - ); - return; - } - Err(err) => { - self.add_error_message(format!( - "Failed to resolve session object storage URL: {err}" - )); - return; - } - }; let owner = self .auth_manager .auth_cached() @@ -3841,8 +3825,27 @@ impl ChatWidget { self.add_info_message("Sharing current session…".to_string(), None); + let config = self.config.clone(); let app_event_tx = self.app_event_tx.clone(); tokio::spawn(async move { + let storage_url = match config.resolve_session_object_storage_url().await { + Ok(Some(url)) => url, + Ok(None) => { + let cell = history_cell::new_error_event( + "Sharing requires `session_object_storage_url` or `session_object_storage_url_cmd` in config.toml." + .to_string(), + ); + app_event_tx.send(AppEvent::InsertHistoryCell(Box::new(cell))); + return; + } + Err(err) => { + let cell = history_cell::new_error_event(format!( + "Failed to resolve session object storage URL: {err}" + )); + app_event_tx.send(AppEvent::InsertHistoryCell(Box::new(cell))); + return; + } + }; let result = codex_core::session_share::upload_rollout_with_owner( &storage_url, thread_id, diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 0f3f50eb02..03140aa724 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -582,7 +582,7 @@ async fn run_ratatui_app( match path { Some(path) => resume_picker::SessionSelection::Fork(path), None => { - let storage_url = match config.resolve_session_object_storage_url() { + let storage_url = match config.resolve_session_object_storage_url().await { Ok(Some(url)) => url, Ok(None) => return missing_session_exit(&mut tui, id_str, "fork"), Err(err) => {