mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[app-server] update doc with codex error info (#6941)
Document new codex error info. Also fixed the name from `codex_error_code` to `codex_error_info`.
This commit is contained in:
committed by
GitHub
Unverified
parent
8e5f38c0f0
commit
7e2165f394
@@ -50,6 +50,9 @@ macro_rules! v2_enum_from_core {
|
||||
}
|
||||
|
||||
/// This translation layer make sure that we expose codex error code in camel case.
|
||||
///
|
||||
/// When an upstream HTTP status is available (for example, from the Responses API or a provider),
|
||||
/// it is forwarded in `httpStatusCode` on the relevant `codexErrorInfo` variant.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
@@ -615,7 +618,7 @@ pub struct Turn {
|
||||
#[error("{message}")]
|
||||
pub struct TurnError {
|
||||
pub message: String,
|
||||
pub codex_error_code: Option<CodexErrorInfo>,
|
||||
pub codex_error_info: Option<CodexErrorInfo>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
@@ -1253,7 +1256,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_error_code_serializes_http_status_code_in_camel_case() {
|
||||
fn codex_error_info_serializes_http_status_code_in_camel_case() {
|
||||
let value = CodexErrorInfo::ResponseTooManyFailedAttempts {
|
||||
http_status_code: Some(401),
|
||||
};
|
||||
|
||||
@@ -339,6 +339,29 @@ Event notifications are the server-initiated event stream for thread lifecycles,
|
||||
|
||||
The app-server streams JSON-RPC notifications while a turn is running. Each turn starts with `turn/started` (initial `turn`) and ends with `turn/completed` (final `turn` plus token `usage`), and clients subscribe to the events they care about, rendering each item incrementally as updates arrive. The per-item lifecycle is always: `item/started` → zero or more item-specific deltas → `item/completed`.
|
||||
|
||||
- `turn/started` — `{ turn }` with the turn id, empty `items`, and `status: "inProgress"`.
|
||||
- `turn/completed` — `{ turn }` where `turn.status` is `completed`, `interrupted`, or `failed`; failures carry `{ error: { message, codexErrorInfo? } }`.
|
||||
|
||||
Today both notifications carry an empty `items` array even when item events were streamed; rely on `item/*` notifications for the canonical item list until this is fixed.
|
||||
|
||||
#### Errors
|
||||
`error` event is emitted whenever the server hits an error mid-turn (for example, upstream model errors or quota limits). Carries the same `{ error: { message, codexErrorInfo? } }` payload as `turn.status: "failed"` and may precede that terminal notification.
|
||||
|
||||
`codexErrorInfo` maps to the `CodexErrorInfo` enum. Common values:
|
||||
- `ContextWindowExceeded`
|
||||
- `UsageLimitExceeded`
|
||||
- `HttpConnectionFailed { httpStatusCode? }`: upstream HTTP failures including 4xx/5xx
|
||||
- `ResponseStreamConnectionFailed { httpStatusCode? }`: failure to connect to the response SSE stream
|
||||
- `ResponseStreamDisconnected { httpStatusCode? }`: disconnect of the response SSE stream in the middle of a turn before completion
|
||||
- `ResponseTooManyFailedAttempts { httpStatusCode? }`
|
||||
- `BadRequest`
|
||||
- `Unauthorized`
|
||||
- `SandboxError`
|
||||
- `InternalServerError`
|
||||
- `Other`: all unclassified errors
|
||||
|
||||
When an upstream HTTP status is available (for example, from the Responses API or a provider), it is forwarded in `httpStatusCode` on the relevant `codexErrorInfo` variant.
|
||||
|
||||
#### Thread items
|
||||
|
||||
`ThreadItem` is the tagged union carried in turn responses and `item/*` notifications. Currently we support events for the following items:
|
||||
|
||||
@@ -264,7 +264,7 @@ pub(crate) async fn apply_bespoke_event_handling(
|
||||
EventMsg::Error(ev) => {
|
||||
let turn_error = TurnError {
|
||||
message: ev.message,
|
||||
codex_error_code: ev.codex_error_code.map(V2CodexErrorInfo::from),
|
||||
codex_error_info: ev.codex_error_info.map(V2CodexErrorInfo::from),
|
||||
};
|
||||
handle_error(conversation_id, turn_error.clone(), &turn_summary_store).await;
|
||||
outgoing
|
||||
@@ -278,7 +278,7 @@ pub(crate) async fn apply_bespoke_event_handling(
|
||||
// but we notify the client.
|
||||
let turn_error = TurnError {
|
||||
message: ev.message,
|
||||
codex_error_code: ev.codex_error_code.map(V2CodexErrorInfo::from),
|
||||
codex_error_info: ev.codex_error_info.map(V2CodexErrorInfo::from),
|
||||
};
|
||||
outgoing
|
||||
.send_server_notification(ServerNotification::Error(ErrorNotification {
|
||||
@@ -899,7 +899,7 @@ mod tests {
|
||||
conversation_id,
|
||||
TurnError {
|
||||
message: "boom".to_string(),
|
||||
codex_error_code: Some(V2CodexErrorInfo::InternalServerError),
|
||||
codex_error_info: Some(V2CodexErrorInfo::InternalServerError),
|
||||
},
|
||||
&turn_summary_store,
|
||||
)
|
||||
@@ -910,7 +910,7 @@ mod tests {
|
||||
turn_summary.last_error,
|
||||
Some(TurnError {
|
||||
message: "boom".to_string(),
|
||||
codex_error_code: Some(V2CodexErrorInfo::InternalServerError),
|
||||
codex_error_info: Some(V2CodexErrorInfo::InternalServerError),
|
||||
})
|
||||
);
|
||||
Ok(())
|
||||
@@ -956,7 +956,7 @@ mod tests {
|
||||
conversation_id,
|
||||
TurnError {
|
||||
message: "oops".to_string(),
|
||||
codex_error_code: None,
|
||||
codex_error_info: None,
|
||||
},
|
||||
&turn_summary_store,
|
||||
)
|
||||
@@ -996,7 +996,7 @@ mod tests {
|
||||
conversation_id,
|
||||
TurnError {
|
||||
message: "bad".to_string(),
|
||||
codex_error_code: Some(V2CodexErrorInfo::Other),
|
||||
codex_error_info: Some(V2CodexErrorInfo::Other),
|
||||
},
|
||||
&turn_summary_store,
|
||||
)
|
||||
@@ -1024,7 +1024,7 @@ mod tests {
|
||||
TurnStatus::Failed {
|
||||
error: TurnError {
|
||||
message: "bad".to_string(),
|
||||
codex_error_code: Some(V2CodexErrorInfo::Other),
|
||||
codex_error_info: Some(V2CodexErrorInfo::Other),
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -1079,7 +1079,7 @@ mod tests {
|
||||
conversation_a,
|
||||
TurnError {
|
||||
message: "a1".to_string(),
|
||||
codex_error_code: Some(V2CodexErrorInfo::BadRequest),
|
||||
codex_error_info: Some(V2CodexErrorInfo::BadRequest),
|
||||
},
|
||||
&turn_summary_store,
|
||||
)
|
||||
@@ -1098,7 +1098,7 @@ mod tests {
|
||||
conversation_b,
|
||||
TurnError {
|
||||
message: "b1".to_string(),
|
||||
codex_error_code: None,
|
||||
codex_error_info: None,
|
||||
},
|
||||
&turn_summary_store,
|
||||
)
|
||||
@@ -1134,7 +1134,7 @@ mod tests {
|
||||
TurnStatus::Failed {
|
||||
error: TurnError {
|
||||
message: "a1".to_string(),
|
||||
codex_error_code: Some(V2CodexErrorInfo::BadRequest),
|
||||
codex_error_info: Some(V2CodexErrorInfo::BadRequest),
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -1155,7 +1155,7 @@ mod tests {
|
||||
TurnStatus::Failed {
|
||||
error: TurnError {
|
||||
message: "b1".to_string(),
|
||||
codex_error_code: None,
|
||||
codex_error_info: None,
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1191,12 +1191,12 @@ impl Session {
|
||||
message: impl Into<String>,
|
||||
codex_error: CodexErr,
|
||||
) {
|
||||
let codex_error_code = CodexErrorInfo::ResponseStreamDisconnected {
|
||||
let codex_error_info = CodexErrorInfo::ResponseStreamDisconnected {
|
||||
http_status_code: codex_error.http_status_code_value(),
|
||||
};
|
||||
let event = EventMsg::StreamError(StreamErrorEvent {
|
||||
message: message.into(),
|
||||
codex_error_code: Some(codex_error_code),
|
||||
codex_error_info: Some(codex_error_info),
|
||||
});
|
||||
self.send_event(turn_context, event).await;
|
||||
}
|
||||
@@ -1689,7 +1689,7 @@ mod handlers {
|
||||
id: sub_id.clone(),
|
||||
msg: EventMsg::Error(ErrorEvent {
|
||||
message: "Failed to shutdown rollout recorder".to_string(),
|
||||
codex_error_code: Some(CodexErrorInfo::Other),
|
||||
codex_error_info: Some(CodexErrorInfo::Other),
|
||||
}),
|
||||
};
|
||||
sess.send_event_raw(event).await;
|
||||
|
||||
@@ -469,7 +469,7 @@ impl CodexErr {
|
||||
};
|
||||
ErrorEvent {
|
||||
message,
|
||||
codex_error_code: Some(self.to_codex_protocol_error()),
|
||||
codex_error_info: Some(self.to_codex_protocol_error()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -650,7 +650,7 @@ mod tests {
|
||||
"prefix: Error while reading the server response: HTTP status client error (429 Too Many Requests) for url (http://example.com/), request id: req-123"
|
||||
);
|
||||
assert_eq!(
|
||||
event.codex_error_code,
|
||||
event.codex_error_info,
|
||||
Some(CodexErrorInfo::ResponseStreamConnectionFailed {
|
||||
http_status_code: Some(429)
|
||||
})
|
||||
|
||||
@@ -540,7 +540,7 @@ fn error_event_produces_error() {
|
||||
"e1",
|
||||
EventMsg::Error(codex_core::protocol::ErrorEvent {
|
||||
message: "boom".to_string(),
|
||||
codex_error_code: Some(CodexErrorInfo::Other),
|
||||
codex_error_info: Some(CodexErrorInfo::Other),
|
||||
}),
|
||||
));
|
||||
assert_eq!(
|
||||
@@ -580,7 +580,7 @@ fn stream_error_event_produces_error() {
|
||||
"e1",
|
||||
EventMsg::StreamError(codex_core::protocol::StreamErrorEvent {
|
||||
message: "retrying".to_string(),
|
||||
codex_error_code: Some(CodexErrorInfo::Other),
|
||||
codex_error_info: Some(CodexErrorInfo::Other),
|
||||
}),
|
||||
));
|
||||
assert_eq!(
|
||||
@@ -599,7 +599,7 @@ fn error_followed_by_task_complete_produces_turn_failed() {
|
||||
"e1",
|
||||
EventMsg::Error(ErrorEvent {
|
||||
message: "boom".to_string(),
|
||||
codex_error_code: Some(CodexErrorInfo::Other),
|
||||
codex_error_info: Some(CodexErrorInfo::Other),
|
||||
}),
|
||||
);
|
||||
assert_eq!(
|
||||
|
||||
@@ -716,7 +716,7 @@ pub struct ExitedReviewModeEvent {
|
||||
pub struct ErrorEvent {
|
||||
pub message: String,
|
||||
#[serde(default)]
|
||||
pub codex_error_code: Option<CodexErrorInfo>,
|
||||
pub codex_error_info: Option<CodexErrorInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, TS)]
|
||||
@@ -1395,7 +1395,7 @@ pub struct UndoCompletedEvent {
|
||||
pub struct StreamErrorEvent {
|
||||
pub message: String,
|
||||
#[serde(default)]
|
||||
pub codex_error_code: Option<CodexErrorInfo>,
|
||||
pub codex_error_info: Option<CodexErrorInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, TS)]
|
||||
|
||||
@@ -2648,7 +2648,7 @@ fn stream_error_updates_status_indicator() {
|
||||
id: "sub-1".into(),
|
||||
msg: EventMsg::StreamError(StreamErrorEvent {
|
||||
message: msg.to_string(),
|
||||
codex_error_code: Some(CodexErrorInfo::Other),
|
||||
codex_error_info: Some(CodexErrorInfo::Other),
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user