mirror of
https://github.com/openai/codex.git
synced 2026-10-31 19:28:49 +00:00
Treat audio validation failures as non-retryable input errors (#58434)
## What changed Map audio size, data, format, and sample-rate validation failures to `InvalidAudioInput` for HTTP 400 responses and streamed errors. Preserve the provider's message, falling back to `Invalid audio input.` when it is absent or blank. Classify these errors as non-retryable and expose them as `BadRequest`. Redact their messages in doctor diagnostics and group them as invalid requests in telemetry. ## Testing Add coverage for all six audio validation codes across HTTP errors, streamed `error` events, and `response.failed` events, checking error classification, message preservation, and the absence of retries. Extend doctor diagnostic redaction coverage for `InvalidAudioInput`. GitOrigin-RevId: 0a59964bcabd83997bdb32fc9318a5482dac33b0
This commit is contained in:
@@ -37,6 +37,7 @@ pub fn map_api_error(err: ApiError) -> CodexErr {
|
||||
| ApiError::FlexUnavailable
|
||||
| ApiError::RateLimit(_)
|
||||
| ApiError::InvalidRequest { .. }
|
||||
| ApiError::InvalidAudioInput { .. }
|
||||
| ApiError::ModelNotFound { .. }
|
||||
| ApiError::InvalidPrompt { .. }
|
||||
| ApiError::CyberPolicy { .. }
|
||||
@@ -83,6 +84,9 @@ fn map_api_error_details(err: ApiError) -> CodexErr {
|
||||
})
|
||||
}
|
||||
ApiError::InvalidRequest { message } => CodexErr::InvalidRequest(message),
|
||||
ApiError::InvalidAudioInput { message } => {
|
||||
CodexErr::new(CodexErrorDetails::InvalidAudioInput { message })
|
||||
}
|
||||
ApiError::ModelNotFound { message, status } => {
|
||||
CodexErr::new(CodexErrorDetails::ModelNotFound { message, status })
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ pub enum ApiError {
|
||||
#[error("invalid request: {message}")]
|
||||
InvalidRequest { message: String },
|
||||
#[error("{message}")]
|
||||
InvalidAudioInput { message: String },
|
||||
#[error("{message}")]
|
||||
ModelNotFound {
|
||||
message: String,
|
||||
status: Option<StatusCode>,
|
||||
@@ -77,6 +79,22 @@ pub(crate) fn parse_terminal_provider_error(
|
||||
let error = error?;
|
||||
let code = error.get("code")?.as_str()?;
|
||||
match (code, status) {
|
||||
(
|
||||
"audio_too_large"
|
||||
| "audio_too_short"
|
||||
| "invalid_audio"
|
||||
| "invalid_audio_data"
|
||||
| "invalid_audio_format"
|
||||
| "missing_audio_sample_rate",
|
||||
None | Some(StatusCode::BAD_REQUEST),
|
||||
) => Some(ApiError::InvalidAudioInput {
|
||||
message: error
|
||||
.get("message")
|
||||
.and_then(Value::as_str)
|
||||
.filter(|message| !message.trim().is_empty())
|
||||
.unwrap_or("Invalid audio input.")
|
||||
.to_owned(),
|
||||
}),
|
||||
("flex_unavailable", None | Some(StatusCode::TOO_MANY_REQUESTS)) => {
|
||||
Some(ApiError::FlexUnavailable)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,47 @@ use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn audio_validation_codes_map_to_non_retryable_audio_input_errors_on_all_transports() {
|
||||
for code in [
|
||||
"audio_too_large",
|
||||
"audio_too_short",
|
||||
"invalid_audio",
|
||||
"invalid_audio_data",
|
||||
"invalid_audio_format",
|
||||
"missing_audio_sample_rate",
|
||||
] {
|
||||
let detail = json!({"code": code, "message": "Invalid recording."});
|
||||
let http_error = map_api_error(crate::error::ApiError::Transport(
|
||||
crate::TransportError::Http {
|
||||
status: StatusCode::BAD_REQUEST,
|
||||
url: None,
|
||||
headers: None,
|
||||
body: Some(json!({"error": detail}).to_string()),
|
||||
retry_after: None,
|
||||
},
|
||||
));
|
||||
let mut errors = vec![http_error];
|
||||
for failure in [
|
||||
json!({"type": "error", "error": detail}),
|
||||
json!({"type": "response.failed", "response": {"error": detail}}),
|
||||
] {
|
||||
let event = serde_json::from_value(failure).unwrap();
|
||||
errors.push(map_api_error(
|
||||
process_responses_event(event).unwrap_err().into_api_error(),
|
||||
));
|
||||
}
|
||||
for error in errors {
|
||||
assert_eq!(error.retry_delay(/*retry_count*/ 1), None);
|
||||
assert!(matches!(
|
||||
error.details(),
|
||||
codex_protocol::error::CodexErrorDetails::InvalidAudioInput { .. }
|
||||
));
|
||||
assert_eq!(error.to_string(), "Invalid recording.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Only the explicit policy capacity code changes overload attribution.
|
||||
#[tokio::test(start_paused = true)]
|
||||
async fn policy_capacity_classification_preserves_overload_retry_behavior() {
|
||||
|
||||
Reference in New Issue
Block a user