mirror of
https://github.com/openai/codex.git
synced 2026-10-29 19:08:43 +00:00
## Why MCP request failures and tool errors reported only through `codex/telemetry` metadata previously received an `unknown` error code, obscuring the failure category. ## What changed - Classify typed request failures into bounded codes such as `request_timeout`, `transport_closed`, and `protocol_error`, and carry them into MCP call metrics and spans before errors become strings. - Preserve typed timeouts when a prepared tool call exhausts its deadline during trusted access lookup. - Accept allowlisted `error_code` values from `codex/telemetry` as a fallback after structured content and connector authentication metadata. Keep model output unchanged and ignore unrecognized metadata codes. ## Testing Add coverage for typed errors wrapped in context, prepared-call timeout classification, metadata precedence and rejection, unchanged model output, and exported span error attributes. GitOrigin-RevId: d7a440a6c9119b8a07a7f1fad697eb25af506f00
49 lines
1.8 KiB
Rust
49 lines
1.8 KiB
Rust
//! Access structured MCP errors without dropping their original protocol data.
|
|
|
|
use anyhow::Error;
|
|
use rmcp::ErrorData;
|
|
use rmcp::service::ServiceError;
|
|
use std::time::Duration;
|
|
|
|
use crate::rmcp_client::ClientOperationError;
|
|
|
|
/// Returns the server's structured protocol error, including its original data.
|
|
pub fn mcp_error(error: &Error) -> Option<&ErrorData> {
|
|
let ClientOperationError::Service(ServiceError::McpError(error)) = error.downcast_ref()? else {
|
|
return None;
|
|
};
|
|
Some(error)
|
|
}
|
|
|
|
/// Preserves a request deadline as a typed error for caller-side telemetry.
|
|
pub fn request_timeout_error(label: &str, duration: Duration) -> Error {
|
|
ClientOperationError::Timeout {
|
|
label: label.to_string(),
|
|
duration,
|
|
}
|
|
.into()
|
|
}
|
|
|
|
/// Returns a bounded caller-side failure category without parsing error text.
|
|
pub fn request_error_code(error: &Error) -> Option<&'static str> {
|
|
let error = error.downcast_ref::<ClientOperationError>()?;
|
|
Some(match error {
|
|
ClientOperationError::Timeout { .. } => "request_timeout",
|
|
ClientOperationError::Service(error) => match error {
|
|
ServiceError::Timeout { .. } => "request_timeout",
|
|
ServiceError::TransportClosed => "transport_closed",
|
|
ServiceError::TransportSend(_) => "transport_error",
|
|
ServiceError::McpError(_) => "protocol_error",
|
|
ServiceError::UnexpectedResponse => "unexpected_response",
|
|
ServiceError::Cancelled { .. } => "cancelled",
|
|
ServiceError::SubscriptionLagged { .. } => "subscription_lagged",
|
|
ServiceError::InputRequiredRoundsExceeded { .. } => "input_required_rounds_exceeded",
|
|
_ => return None,
|
|
},
|
|
})
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[path = "service_error_tests.rs"]
|
|
mod tests;
|