Include request ID in the error message (#4572)
To help with issue debugging <img width="1414" height="253" alt="image" src="https://github.com/user-attachments/assets/254732df-44ac-4252-997a-6c5e0927355b" />
This commit is contained in:
@@ -6,6 +6,8 @@ use crate::client_common::ResponseEvent;
|
|||||||
use crate::client_common::ResponseStream;
|
use crate::client_common::ResponseStream;
|
||||||
use crate::error::CodexErr;
|
use crate::error::CodexErr;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
|
use crate::error::RetryLimitReachedError;
|
||||||
|
use crate::error::UnexpectedResponseError;
|
||||||
use crate::model_family::ModelFamily;
|
use crate::model_family::ModelFamily;
|
||||||
use crate::openai_tools::create_tools_json_for_chat_completions_api;
|
use crate::openai_tools::create_tools_json_for_chat_completions_api;
|
||||||
use crate::util::backoff;
|
use crate::util::backoff;
|
||||||
@@ -320,11 +322,18 @@ pub(crate) async fn stream_chat_completions(
|
|||||||
let status = res.status();
|
let status = res.status();
|
||||||
if !(status == StatusCode::TOO_MANY_REQUESTS || status.is_server_error()) {
|
if !(status == StatusCode::TOO_MANY_REQUESTS || status.is_server_error()) {
|
||||||
let body = (res.text().await).unwrap_or_default();
|
let body = (res.text().await).unwrap_or_default();
|
||||||
return Err(CodexErr::UnexpectedStatus(status, body));
|
return Err(CodexErr::UnexpectedStatus(UnexpectedResponseError {
|
||||||
|
status,
|
||||||
|
body,
|
||||||
|
request_id: None,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if attempt > max_retries {
|
if attempt > max_retries {
|
||||||
return Err(CodexErr::RetryLimit(status));
|
return Err(CodexErr::RetryLimit(RetryLimitReachedError {
|
||||||
|
status,
|
||||||
|
request_id: None,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let retry_after_secs = res
|
let retry_after_secs = res
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ use std::time::Duration;
|
|||||||
|
|
||||||
use crate::AuthManager;
|
use crate::AuthManager;
|
||||||
use crate::auth::CodexAuth;
|
use crate::auth::CodexAuth;
|
||||||
|
use crate::error::RetryLimitReachedError;
|
||||||
|
use crate::error::UnexpectedResponseError;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use codex_app_server_protocol::AuthMode;
|
use codex_app_server_protocol::AuthMode;
|
||||||
use codex_protocol::ConversationId;
|
use codex_protocol::ConversationId;
|
||||||
@@ -307,14 +309,17 @@ impl ModelClient {
|
|||||||
.log_request(attempt, || req_builder.send())
|
.log_request(attempt, || req_builder.send())
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
let mut request_id = None;
|
||||||
if let Ok(resp) = &res {
|
if let Ok(resp) = &res {
|
||||||
|
request_id = resp
|
||||||
|
.headers()
|
||||||
|
.get("cf-ray")
|
||||||
|
.map(|v| v.to_str().unwrap_or_default().to_string());
|
||||||
|
|
||||||
trace!(
|
trace!(
|
||||||
"Response status: {}, cf-ray: {}",
|
"Response status: {}, cf-ray: {:?}",
|
||||||
resp.status(),
|
resp.status(),
|
||||||
resp.headers()
|
request_id
|
||||||
.get("cf-ray")
|
|
||||||
.map(|v| v.to_str().unwrap_or_default())
|
|
||||||
.unwrap_or_default()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,7 +379,11 @@ impl ModelClient {
|
|||||||
// Surface the error body to callers. Use `unwrap_or_default` per Clippy.
|
// Surface the error body to callers. Use `unwrap_or_default` per Clippy.
|
||||||
let body = res.text().await.unwrap_or_default();
|
let body = res.text().await.unwrap_or_default();
|
||||||
return Err(StreamAttemptError::Fatal(CodexErr::UnexpectedStatus(
|
return Err(StreamAttemptError::Fatal(CodexErr::UnexpectedStatus(
|
||||||
status, body,
|
UnexpectedResponseError {
|
||||||
|
status,
|
||||||
|
body,
|
||||||
|
request_id: None,
|
||||||
|
},
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,6 +414,7 @@ impl ModelClient {
|
|||||||
Err(StreamAttemptError::RetryableHttpError {
|
Err(StreamAttemptError::RetryableHttpError {
|
||||||
status,
|
status,
|
||||||
retry_after,
|
retry_after,
|
||||||
|
request_id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Err(e) => Err(StreamAttemptError::RetryableTransportError(e.into())),
|
Err(e) => Err(StreamAttemptError::RetryableTransportError(e.into())),
|
||||||
@@ -448,6 +458,7 @@ enum StreamAttemptError {
|
|||||||
RetryableHttpError {
|
RetryableHttpError {
|
||||||
status: StatusCode,
|
status: StatusCode,
|
||||||
retry_after: Option<Duration>,
|
retry_after: Option<Duration>,
|
||||||
|
request_id: Option<String>,
|
||||||
},
|
},
|
||||||
RetryableTransportError(CodexErr),
|
RetryableTransportError(CodexErr),
|
||||||
Fatal(CodexErr),
|
Fatal(CodexErr),
|
||||||
@@ -472,11 +483,13 @@ impl StreamAttemptError {
|
|||||||
|
|
||||||
fn into_error(self) -> CodexErr {
|
fn into_error(self) -> CodexErr {
|
||||||
match self {
|
match self {
|
||||||
Self::RetryableHttpError { status, .. } => {
|
Self::RetryableHttpError {
|
||||||
|
status, request_id, ..
|
||||||
|
} => {
|
||||||
if status == StatusCode::INTERNAL_SERVER_ERROR {
|
if status == StatusCode::INTERNAL_SERVER_ERROR {
|
||||||
CodexErr::InternalServerError
|
CodexErr::InternalServerError
|
||||||
} else {
|
} else {
|
||||||
CodexErr::RetryLimit(status)
|
CodexErr::RetryLimit(RetryLimitReachedError { status, request_id })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::RetryableTransportError(error) => error,
|
Self::RetryableTransportError(error) => error,
|
||||||
|
|||||||
@@ -76,8 +76,8 @@ pub enum CodexErr {
|
|||||||
Interrupted,
|
Interrupted,
|
||||||
|
|
||||||
/// Unexpected HTTP status code.
|
/// Unexpected HTTP status code.
|
||||||
#[error("unexpected status {0}: {1}")]
|
#[error("{0}")]
|
||||||
UnexpectedStatus(StatusCode, String),
|
UnexpectedStatus(UnexpectedResponseError),
|
||||||
|
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
UsageLimitReached(UsageLimitReachedError),
|
UsageLimitReached(UsageLimitReachedError),
|
||||||
@@ -91,8 +91,8 @@ pub enum CodexErr {
|
|||||||
InternalServerError,
|
InternalServerError,
|
||||||
|
|
||||||
/// Retry limit exceeded.
|
/// Retry limit exceeded.
|
||||||
#[error("exceeded retry limit, last status: {0}")]
|
#[error("{0}")]
|
||||||
RetryLimit(StatusCode),
|
RetryLimit(RetryLimitReachedError),
|
||||||
|
|
||||||
/// Agent loop died unexpectedly
|
/// Agent loop died unexpectedly
|
||||||
#[error("internal error; agent loop died unexpectedly")]
|
#[error("internal error; agent loop died unexpectedly")]
|
||||||
@@ -135,6 +135,49 @@ pub enum CodexErr {
|
|||||||
EnvVar(EnvVarError),
|
EnvVar(EnvVarError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct UnexpectedResponseError {
|
||||||
|
pub status: StatusCode,
|
||||||
|
pub body: String,
|
||||||
|
pub request_id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for UnexpectedResponseError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"unexpected status {}: {}{}",
|
||||||
|
self.status,
|
||||||
|
self.body,
|
||||||
|
self.request_id
|
||||||
|
.as_ref()
|
||||||
|
.map(|id| format!(", request id: {id}"))
|
||||||
|
.unwrap_or_default()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for UnexpectedResponseError {}
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RetryLimitReachedError {
|
||||||
|
pub status: StatusCode,
|
||||||
|
pub request_id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for RetryLimitReachedError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"exceeded retry limit, last status: {}{}",
|
||||||
|
self.status,
|
||||||
|
self.request_id
|
||||||
|
.as_ref()
|
||||||
|
.map(|id| format!(", request id: {id}"))
|
||||||
|
.unwrap_or_default()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UsageLimitReachedError {
|
pub struct UsageLimitReachedError {
|
||||||
pub(crate) plan_type: Option<PlanType>,
|
pub(crate) plan_type: Option<PlanType>,
|
||||||
|
|||||||
Reference in New Issue
Block a user