Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 1 addition & 90 deletions codex-rs/analytics/src/facts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use codex_protocol::config_types::Personality;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::config_types::ServiceTier;
use codex_protocol::error::CodexErr;
pub use codex_protocol::error::CodexErrKind;
use codex_protocol::models::PermissionProfile;
use codex_protocol::openai_models::ReasoningEffort;
use codex_protocol::protocol::AskForApproval;
Expand Down Expand Up @@ -139,48 +140,6 @@ impl TurnCodexErrorFact {
}
}

#[derive(Clone, Copy, Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum CodexErrKind {
TurnAborted,
SessionBudgetExceeded,
Stream,
ContextWindowExceeded,
ThreadNotFound,
AgentLimitReached,
SessionConfiguredNotFirstEvent,
Timeout,
RequestTimeout,
Spawn,
Interrupted,
UnexpectedStatus,
InvalidRequest,
InvalidImageRequest,
UsageLimitReached,
ServerOverloaded,
CyberPolicy,
ResponseStreamFailed,
ConnectionFailed,
QuotaExceeded,
UsageNotIncluded,
InternalServerError,
RetryLimit,
InternalAgentDied,
Sandbox,
LandlockSandboxExecutableNotProvided,
UnsupportedOperation,
RefreshTokenFailed,
Fatal,
Io,
Json,
#[cfg(target_os = "linux")]
LandlockRuleset,
#[cfg(target_os = "linux")]
LandlockPathFd,
TokioJoin,
EnvVar,
}

#[derive(Clone)]
pub(crate) struct TurnCodexError {
pub(crate) kind: CodexErrKind,
Expand All @@ -196,54 +155,6 @@ impl TurnCodexError {
}
}

impl From<&CodexErr> for CodexErrKind {
fn from(error: &CodexErr) -> Self {
match error {
CodexErr::TurnAborted => CodexErrKind::TurnAborted,
CodexErr::SessionBudgetExceeded => CodexErrKind::SessionBudgetExceeded,
CodexErr::Stream(..) => CodexErrKind::Stream,
CodexErr::ContextWindowExceeded => CodexErrKind::ContextWindowExceeded,
CodexErr::ThreadNotFound(_) => CodexErrKind::ThreadNotFound,
CodexErr::AgentLimitReached { .. } => CodexErrKind::AgentLimitReached,
CodexErr::SessionConfiguredNotFirstEvent => {
CodexErrKind::SessionConfiguredNotFirstEvent
}
CodexErr::Timeout => CodexErrKind::Timeout,
CodexErr::RequestTimeout => CodexErrKind::RequestTimeout,
CodexErr::Spawn => CodexErrKind::Spawn,
CodexErr::Interrupted => CodexErrKind::Interrupted,
CodexErr::UnexpectedStatus(_) => CodexErrKind::UnexpectedStatus,
CodexErr::InvalidRequest(_) => CodexErrKind::InvalidRequest,
CodexErr::InvalidImageRequest() => CodexErrKind::InvalidImageRequest,
CodexErr::UsageLimitReached(_) => CodexErrKind::UsageLimitReached,
CodexErr::ServerOverloaded => CodexErrKind::ServerOverloaded,
CodexErr::CyberPolicy { .. } => CodexErrKind::CyberPolicy,
CodexErr::ResponseStreamFailed(_) => CodexErrKind::ResponseStreamFailed,
CodexErr::ConnectionFailed(_) => CodexErrKind::ConnectionFailed,
CodexErr::QuotaExceeded => CodexErrKind::QuotaExceeded,
CodexErr::UsageNotIncluded => CodexErrKind::UsageNotIncluded,
CodexErr::InternalServerError => CodexErrKind::InternalServerError,
CodexErr::RetryLimit(_) => CodexErrKind::RetryLimit,
CodexErr::InternalAgentDied => CodexErrKind::InternalAgentDied,
CodexErr::Sandbox(_) => CodexErrKind::Sandbox,
CodexErr::LandlockSandboxExecutableNotProvided => {
CodexErrKind::LandlockSandboxExecutableNotProvided
}
CodexErr::UnsupportedOperation(_) => CodexErrKind::UnsupportedOperation,
CodexErr::RefreshTokenFailed(_) => CodexErrKind::RefreshTokenFailed,
CodexErr::Fatal(_) => CodexErrKind::Fatal,
CodexErr::Io(_) => CodexErrKind::Io,
CodexErr::Json(_) => CodexErrKind::Json,
#[cfg(target_os = "linux")]
CodexErr::LandlockRuleset(_) => CodexErrKind::LandlockRuleset,
#[cfg(target_os = "linux")]
CodexErr::LandlockPathFd(_) => CodexErrKind::LandlockPathFd,
CodexErr::TokioJoin(_) => CodexErrKind::TokioJoin,
CodexErr::EnvVar(_) => CodexErrKind::EnvVar,
}
}
}

#[derive(Clone, Copy, Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum TurnStatus {
Expand Down
7 changes: 4 additions & 3 deletions codex-rs/app-server/src/request_processors/request_errors.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use super::*;
use codex_protocol::error::CodexErrorDetails;

pub(super) fn environment_selection_error(err: CodexErr) -> JSONRPCErrorError {
match err {
CodexErr::InvalidRequest(message) => invalid_request(message),
err => internal_error(format!("failed to validate environment selections: {err}")),
match err.details() {
CodexErrorDetails::InvalidRequest(message) => invalid_request(message.clone()),
_ => internal_error(format!("failed to validate environment selections: {err}")),
}
}
41 changes: 22 additions & 19 deletions codex-rs/app-server/src/request_processors/thread_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::error_code::method_not_found;
use codex_app_server_protocol::SelectedCapabilityRoot;
use codex_extension_api::ExtensionDataInit;
use codex_protocol::config_types::MultiAgentMode;
use codex_protocol::error::CodexErrorDetails;
use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_DANGER_FULL_ACCESS;
use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_WORKSPACE;
use codex_protocol::protocol::ThreadHistoryMode;
Expand Down Expand Up @@ -1253,10 +1254,12 @@ impl ThreadRequestProcessor {
thread_start.dynamic_tool_count = dynamic_tool_count,
))
.await
.map_err(|err| match err {
CodexErr::InvalidRequest(message) => invalid_request(message),
CodexErr::UnsupportedOperation(message) => method_not_found(message),
err => internal_error(format!("error creating thread: {err}")),
.map_err(|err| match err.details() {
CodexErrorDetails::InvalidRequest(message) => invalid_request(message.clone()),
CodexErrorDetails::UnsupportedOperation(message) => {
method_not_found(message.clone())
}
_ => internal_error(format!("error creating thread: {err}")),
})?;
let session_telemetry = thread.session_telemetry();
session_telemetry.record_startup_phase(
Expand Down Expand Up @@ -1553,9 +1556,9 @@ impl ThreadRequestProcessor {
let count = thread
.decrement_out_of_band_elicitation_count()
.await
.map_err(|err| match err {
CodexErr::InvalidRequest(message) => invalid_request(message),
err => internal_error(format!(
.map_err(|err| match err.details() {
CodexErrorDetails::InvalidRequest(message) => invalid_request(message.clone()),
_ => internal_error(format!(
"failed to decrement out-of-band elicitation counter: {err}"
)),
})?;
Expand Down Expand Up @@ -3399,9 +3402,9 @@ impl ThreadRequestProcessor {
.await;
}
Err(err) => {
let error = match err {
CodexErr::InvalidRequest(message) => invalid_request(message),
err => internal_error(format!("error resuming thread: {err}")),
let error = match err.details() {
CodexErrorDetails::InvalidRequest(message) => invalid_request(message.clone()),
_ => internal_error(format!("error resuming thread: {err}")),
};
self.outgoing.send_error(request_id, error).await;
}
Expand Down Expand Up @@ -4129,12 +4132,12 @@ impl ThreadRequestProcessor {
supports_openai_form_elicitation,
)
.await
.map_err(|err| match err {
CodexErr::Io(_) | CodexErr::Json(_) => {
.map_err(|err| match err.details() {
CodexErrorDetails::Io(_) | CodexErrorDetails::Json(_) => {
invalid_request(format!("failed to load thread {source_thread_id}: {err}"))
}
CodexErr::InvalidRequest(message) => invalid_request(message),
err => internal_error(format!("error forking thread: {err}")),
CodexErrorDetails::InvalidRequest(message) => invalid_request(message.clone()),
_ => internal_error(format!("error forking thread: {err}")),
})?;

Self::set_app_server_client_info(
Expand Down Expand Up @@ -4932,13 +4935,13 @@ fn conversation_summary_rollout_path_read_error(
}

pub(super) fn core_thread_write_error(operation: &str, err: CodexErr) -> JSONRPCErrorError {
match err {
CodexErr::ThreadNotFound(thread_id) => {
match err.details() {
CodexErrorDetails::ThreadNotFound(thread_id) => {
invalid_request(format!("thread not found: {thread_id}"))
}
CodexErr::InvalidRequest(message) => invalid_request(message),
CodexErr::UnsupportedOperation(message) => method_not_found(message),
err => internal_error(format!("failed to {operation}: {err}")),
CodexErrorDetails::InvalidRequest(message) => invalid_request(message.clone()),
CodexErrorDetails::UnsupportedOperation(message) => method_not_found(message.clone()),
_ => internal_error(format!("failed to {operation}: {err}")),
}
}

Expand Down
7 changes: 4 additions & 3 deletions codex-rs/app-server/src/request_processors/turn_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use super::*;
use codex_agent_extension::AgentInvocation;
use codex_agent_extension::AgentRun;
use codex_agent_extension::AgentRunner;
use codex_protocol::error::CodexErrorDetails;
use codex_protocol::models::ContentItem;
use codex_protocol::models::FunctionCallOutputContentItem;
use codex_protocol::models::PermissionProfile;
Expand Down Expand Up @@ -890,9 +891,9 @@ impl TurnRequestProcessor {
thread
.inject_response_items(items)
.await
.map_err(|err| match err {
CodexErr::InvalidRequest(message) => invalid_request(message),
err => internal_error(format!("failed to inject response items: {err}")),
.map_err(|err| match err.details() {
CodexErrorDetails::InvalidRequest(message) => invalid_request(message.clone()),
_ => internal_error(format!("failed to inject response items: {err}")),
})?;
Ok(ThreadInjectItemsResponse {})
}
Expand Down
23 changes: 15 additions & 8 deletions codex-rs/codex-api/src/api_bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use chrono::DateTime;
use chrono::Utc;
use codex_protocol::auth::PlanType;
use codex_protocol::error::CodexErr;
use codex_protocol::error::CodexErrorDetails;
use codex_protocol::error::RetryLimitReachedError;
use codex_protocol::error::UnexpectedResponseError;
use codex_protocol::error::UsageLimitReachedError;
Expand All @@ -20,8 +21,14 @@ pub fn map_api_error(err: ApiError) -> CodexErr {
ApiError::ContextWindowExceeded => CodexErr::ContextWindowExceeded,
ApiError::QuotaExceeded => CodexErr::QuotaExceeded,
ApiError::UsageNotIncluded => CodexErr::UsageNotIncluded,
ApiError::Retryable { message, delay } => CodexErr::Stream(message, delay),
ApiError::Stream(msg) => CodexErr::Stream(msg, None),
ApiError::Retryable { message, delay } => {
let error = CodexErr::Stream(message);
match delay {
Some(delay) => error.with_retry_delay(delay),
None => error,
}
}
ApiError::Stream(msg) => CodexErr::Stream(msg),
ApiError::ServerOverloaded => CodexErr::ServerOverloaded,
ApiError::Api { status, message } => {
let user_message = api_error_user_message(status, &message);
Expand All @@ -37,7 +44,9 @@ pub fn map_api_error(err: ApiError) -> CodexErr {
})
}
ApiError::InvalidRequest { message } => CodexErr::InvalidRequest(message),
ApiError::CyberPolicy { message } => CodexErr::CyberPolicy { message },
ApiError::CyberPolicy { message } => {
CodexErr::new(CodexErrorDetails::CyberPolicy { message })
}
ApiError::Transport(transport) => match transport {
TransportError::Http {
status,
Expand Down Expand Up @@ -72,7 +81,7 @@ pub fn map_api_error(err: ApiError) -> CodexErr {
.filter(|message| !message.trim().is_empty())
.map(str::to_string)
.unwrap_or_else(|| CYBER_POLICY_FALLBACK_MESSAGE.to_string());
CodexErr::CyberPolicy { message }
CodexErr::new(CodexErrorDetails::CyberPolicy { message })
} else if body_text
.contains("The image data you provided does not represent a valid image")
{
Expand Down Expand Up @@ -139,11 +148,9 @@ pub fn map_api_error(err: ApiError) -> CodexErr {
request_id: None,
}),
TransportError::Timeout => CodexErr::RequestTimeout,
TransportError::Network(msg) | TransportError::Build(msg) => {
CodexErr::Stream(msg, None)
}
TransportError::Network(msg) | TransportError::Build(msg) => CodexErr::Stream(msg),
},
ApiError::RateLimit(msg) => CodexErr::Stream(msg, None),
ApiError::RateLimit(msg) => CodexErr::Stream(msg),
}
}

Expand Down
Loading
Loading