mirror of
https://github.com/openai/codex.git
synced 2026-10-28 18:58:46 +00:00
## What changed - Recognize `policy_capacity_unavailable` in HTTP and streaming API errors and expose it as `policyCapacityUnavailable` through the app-server protocol and generated bindings. - Display “Policy-restricted capacity is unavailable. Please try again later.” while preserving overload retry behavior based on `Retry-After`. - Handle the new error in TUI overload rendering, reviewer retries, and error telemetry. ## Testing Add coverage for HTTP and streaming error classification, retry advice, WebSocket terminal error attribution, protocol serialization, and TUI rendering with and without an upstream message. GitOrigin-RevId: 1b9536fba1e11cc891dcf689aee35410d82f5412
89 lines
2.5 KiB
Rust
89 lines
2.5 KiB
Rust
//! Decodes unknown error classifications as `Other` so enclosing records stay readable.
|
|
//! The wire definition keeps serialization exhaustive and known payload validation strict.
|
|
|
|
use crate::protocol::CodexErrorInfo;
|
|
use crate::protocol::NonSteerableTurnKind;
|
|
use serde::Deserialize;
|
|
use serde::Deserializer;
|
|
use serde::Serialize;
|
|
use serde::Serializer;
|
|
use serde::de::Error as _;
|
|
use serde_json::Value;
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
#[serde(
|
|
remote = "CodexErrorInfo",
|
|
rename = "CodexErrorInfo",
|
|
rename_all = "snake_case"
|
|
)]
|
|
enum CodexErrorInfoWire {
|
|
ContextWindowExceeded,
|
|
SessionBudgetExceeded,
|
|
UsageLimitExceeded,
|
|
RateLimitExceeded,
|
|
FlexUnavailable,
|
|
ServerOverloaded,
|
|
ModelNotFound,
|
|
/// Capacity is unavailable on a route restricted by server policy.
|
|
PolicyCapacityUnavailable,
|
|
CyberPolicy,
|
|
BioPolicy,
|
|
MisalignmentPolicyViolation,
|
|
TooManyDenials,
|
|
HttpConnectionFailed {
|
|
http_status_code: Option<u16>,
|
|
},
|
|
ResponseStreamConnectionFailed {
|
|
http_status_code: Option<u16>,
|
|
},
|
|
InternalServerError,
|
|
Unauthorized,
|
|
BadRequest,
|
|
InvalidPrompt,
|
|
SandboxError,
|
|
ResponseStreamDisconnected {
|
|
http_status_code: Option<u16>,
|
|
},
|
|
ResponseTooManyFailedAttempts {
|
|
http_status_code: Option<u16>,
|
|
},
|
|
ActiveTurnNotSteerable {
|
|
turn_kind: NonSteerableTurnKind,
|
|
},
|
|
ThreadRollbackFailed,
|
|
#[serde(other)]
|
|
Other,
|
|
}
|
|
|
|
impl Serialize for CodexErrorInfo {
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: Serializer,
|
|
{
|
|
CodexErrorInfoWire::serialize(self, serializer)
|
|
}
|
|
}
|
|
|
|
impl<'de> Deserialize<'de> for CodexErrorInfo {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
{
|
|
let value = Value::deserialize(deserializer)?;
|
|
// Probe the tag first so only unknown payloads are discarded: `serde(other)`
|
|
// recognizes unknown tags, but its unit variant rejects their payloads.
|
|
if let Value::Object(fields) = &value
|
|
&& fields.len() == 1
|
|
&& let Some(kind) = fields.keys().next()
|
|
&& kind != "other"
|
|
&& matches!(
|
|
CodexErrorInfoWire::deserialize(Value::String(kind.clone())),
|
|
Ok(Self::Other)
|
|
)
|
|
{
|
|
return Ok(Self::Other);
|
|
}
|
|
CodexErrorInfoWire::deserialize(value).map_err(D::Error::custom)
|
|
}
|
|
}
|