From 48cf58233190ce6f2aa6c3194d022c0e273e8fd6 Mon Sep 17 00:00:00 2001 From: jif Date: Wed, 8 Jul 2026 18:58:02 +0100 Subject: [PATCH] Round MCP timeout durations in error messages (#31612) ## Summary MCP operation timeout errors currently print the full debug precision of the remaining timeout budget. That makes a configured 30-second timeout show up as something like `29.999999875s`. This PR rounds the displayed duration to a whole unit, so the error is clean and stable: ```text timed out awaiting tools/list after 30s ``` The timeout behavior itself is unchanged; this only affects the human-facing error text. A regression test covers the reported `tools/list` case. --- codex-rs/rmcp-client/src/rmcp_client.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/codex-rs/rmcp-client/src/rmcp_client.rs b/codex-rs/rmcp-client/src/rmcp_client.rs index c6527990fa..499c8db3bd 100644 --- a/codex-rs/rmcp-client/src/rmcp_client.rs +++ b/codex-rs/rmcp-client/src/rmcp_client.rs @@ -228,7 +228,7 @@ where enum ClientOperationError { #[error(transparent)] Service(#[from] rmcp::service::ServiceError), - #[error("timed out awaiting {label} after {duration:?}")] + #[error("timed out awaiting {label} after {duration:.0?}")] Timeout { label: String, duration: Duration }, } @@ -1219,6 +1219,16 @@ mod tests { use super::*; + #[test] + fn client_operation_timeout_rounds_duration() { + let error = ClientOperationError::Timeout { + label: "tools/list".to_string(), + duration: Duration::from_nanos(29_999_999_875), + }; + + assert_eq!(error.to_string(), "timed out awaiting tools/list after 30s"); + } + #[tokio::test] async fn active_time_timeout_pauses_while_elicitation_is_pending() { let pause_state = ElicitationPauseState::new();