Files
codex/codex-rs/codex-api/src/error.rs
T
Rennie fa8cf44985 Preserve bio policy errors as a distinct non-retryable error (#46306)
## Why

Streaming `bio_policy` failures were classified as generic invalid requests, losing their policy-specific classification.

## What changed

- Add `BioPolicy` errors across the API and core protocol, recognizing streaming failures and HTTP 400 responses, including wrapped WebSocket errors.
- Preserve server messages and use a biological-risk fallback when the message is missing or blank.
- Treat bio policy errors as non-retryable in core and guardian handling, and classify them in diagnostics and telemetry.
- Map `BioPolicy` to `other` in the app-server v2 protocol.

## Testing

Add coverage for error classification, message preservation and fallbacks, HTTP and wrapped WebSocket responses, guardian retry decisions, and app-server conversion. Extend the core integration test to verify that bio policy failures emit a typed error and complete the turn after a single request.

GitOrigin-RevId: 78c2647e8fc8f80297cb8a23fff06ab141099632
2026-09-17 21:25:55 +00:00

54 lines
1.5 KiB
Rust

use crate::rate_limits::RateLimitError;
use codex_client::TransportError;
use codex_protocol::protocol::MisalignmentErrorDetails;
use http::StatusCode;
use std::time::Duration;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ApiError {
#[error(transparent)]
Transport(#[from] TransportError),
#[error("api error {status}: {message}")]
Api { status: StatusCode, message: String },
#[error("stream error: {0}")]
Stream(String),
#[error("context window exceeded")]
ContextWindowExceeded,
#[error("quota exceeded")]
QuotaExceeded,
#[error("usage not included")]
UsageNotIncluded,
#[error("retryable error: {message}")]
Retryable {
message: String,
delay: Option<Duration>,
},
#[error("rate limit exceeded: {message}")]
RateLimitExceeded {
message: String,
delay: Option<Duration>,
},
#[error("rate limit: {0}")]
RateLimit(String),
#[error("invalid request: {message}")]
InvalidRequest { message: String },
#[error("cyber policy: {message}")]
CyberPolicy { message: String },
#[error("bio policy: {message}")]
BioPolicy { message: String },
#[error("misalignment policy violation: {message}")]
MisalignmentPolicyViolation {
message: String,
misalignment: Option<MisalignmentErrorDetails>,
},
#[error("server overloaded")]
ServerOverloaded,
}
impl From<RateLimitError> for ApiError {
fn from(err: RateLimitError) -> Self {
Self::RateLimit(err.to_string())
}
}