mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Handle bio policy errors in Codex (#31439)
## Summary - Treat streamed Responses `bio_policy` failures as terminal invalid requests instead of retryable stream errors. - Recognize the new biology policy code and message in the TUI while preserving the legacy `invalid_prompt` contract. - Keep the existing dedicated biology safety notice and add regression/snapshot coverage for all supported error shapes. ## Why [openai/openai#1068559](https://github.com/openai/openai/pull/1068559) gates a Responses API contract change from `invalid_prompt` and the legacy message to `bio_policy` and new biology copy. Without this compatibility change, streamed blocks are retried as transient failures and the OSS TUI falls back to a generic/raw error instead of the dedicated safety notice. ## Validation - `just test -p codex-api` — 137 passed - `just test -p codex-tui app_server_safety_access_errors_render_dedicated_notice` — passed - `just fix -p codex-api` - `just fix -p codex-tui` - `just fmt` - `just test -p codex-tui` — 2,957 passed; two reproducible failures remain in untouched Guardian feature-flag persistence tests: - `update_feature_flags_disabling_guardian_clears_review_policy_and_restores_default` - `update_feature_flags_disabling_guardian_clears_manual_review_policy_without_history`
This commit is contained in:
@@ -398,7 +398,8 @@ pub fn process_responses_event(
|
||||
} else if is_cyber_policy_error(&error) {
|
||||
let message = cyber_policy_message(error.message);
|
||||
response_error = ApiError::CyberPolicy { message };
|
||||
} else if is_invalid_prompt_error(&error) {
|
||||
} else if matches!(error.code.as_deref(), Some("invalid_prompt" | "bio_policy"))
|
||||
{
|
||||
let message = error
|
||||
.message
|
||||
.unwrap_or_else(|| "Invalid request.".to_string());
|
||||
@@ -632,10 +633,6 @@ fn is_usage_not_included(error: &Error) -> bool {
|
||||
error.code.as_deref() == Some("usage_not_included")
|
||||
}
|
||||
|
||||
fn is_invalid_prompt_error(error: &Error) -> bool {
|
||||
error.code.as_deref() == Some("invalid_prompt")
|
||||
}
|
||||
|
||||
fn is_cyber_policy_error(error: &Error) -> bool {
|
||||
error.code.as_deref() == Some("cyber_policy")
|
||||
}
|
||||
@@ -1076,23 +1073,42 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn invalid_prompt_without_type_is_invalid_request() {
|
||||
let raw_error = r#"{"type":"response.failed","sequence_number":3,"response":{"id":"resp_invalid_prompt_no_type","object":"response","created_at":1759771628,"status":"failed","background":false,"error":{"code":"invalid_prompt","message":"Invalid prompt: we've limited access to this content for safety reasons."},"incomplete_details":null}}"#;
|
||||
async fn content_policy_errors_without_type_are_invalid_requests() {
|
||||
for (code, expected_message) in [
|
||||
(
|
||||
"invalid_prompt",
|
||||
"Invalid prompt: we've limited access to this content for safety reasons.",
|
||||
),
|
||||
(
|
||||
"bio_policy",
|
||||
"This content was flagged for possible biological risk.",
|
||||
),
|
||||
] {
|
||||
let raw_error = json!({
|
||||
"type": "response.failed",
|
||||
"sequence_number": 3,
|
||||
"response": {
|
||||
"id": "resp_content_policy_no_type",
|
||||
"object": "response",
|
||||
"created_at": 1759771628,
|
||||
"status": "failed",
|
||||
"background": false,
|
||||
"error": { "code": code, "message": expected_message },
|
||||
"incomplete_details": null,
|
||||
},
|
||||
})
|
||||
.to_string();
|
||||
let sse1 = format!("event: response.failed\ndata: {raw_error}\n\n");
|
||||
|
||||
let sse1 = format!("event: response.failed\ndata: {raw_error}\n\n");
|
||||
let events = collect_events(&[sse1.as_bytes()]).await;
|
||||
|
||||
let events = collect_events(&[sse1.as_bytes()]).await;
|
||||
|
||||
assert_eq!(events.len(), 1);
|
||||
|
||||
match &events[0] {
|
||||
Err(ApiError::InvalidRequest { message }) => {
|
||||
assert_eq!(
|
||||
message,
|
||||
"Invalid prompt: we've limited access to this content for safety reasons."
|
||||
);
|
||||
assert_eq!(events.len(), 1);
|
||||
match &events[0] {
|
||||
Err(ApiError::InvalidRequest { message }) => {
|
||||
assert_eq!(message, expected_message);
|
||||
}
|
||||
other => panic!("unexpected event for {code}: {other:?}"),
|
||||
}
|
||||
other => panic!("unexpected event: {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user