mirror of
https://github.com/openai/codex.git
synced 2026-11-10 21:00:47 +00:00
## Why An explicit denial from a workspace inference hook should stop the request, including retries and compaction model fallback. ## What changed Recognize `inference_hook_denied` provider errors and preserve the provider's message, with a fallback when none is supplied. Expose the distinct `inferenceHookDenied` error through the app-server protocol and generated schemas and SDK types. Treat the denial as non-retryable during turns, compaction, and guardian review. ## Testing Add coverage for terminal denials over HTTP, SSE, and WebSocket, error serialization and persistence in `thread/read`, and compaction failures that preserve token usage and history without retrying or falling back to another model. GitOrigin-RevId: 2ebd480f01e52f4e6bd3db8a0f92d5814d18ccb1
90 lines
2.5 KiB
Rust
90 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,
|
|
InferenceHookDenied,
|
|
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)
|
|
}
|
|
}
|