openai/codex · error · TurnError
{message}
Error message
{message} What it means
TurnError is the structured failure payload carried in Turn.error (app-server v2 protocol, thread_data.rs) whenever a turn ends with status "failed"; the thiserror #[error("{message}")] attribute only defines its Display, so printing the value yields its message field. It exists so thread/read and turn-event clients receive machine-parsable failure data: message for the human-readable reason, optional codexErrorInfo for structured Codex error info, and optional additionalDetails for extra context.
Source
Thrown at codex-rs/app-server-protocol/src/protocol/v2/thread_data.rs:391
}
#[derive(Default, Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub enum TurnItemsView {
/// `items` was not loaded for this turn. The field is intentionally empty.
NotLoaded,
/// `items` contains only a display summary for this turn.
Summary,
/// `items` contains every ThreadItem available from persisted app-server history for this turn.
#[default]
Full,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS, Error)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
#[error("{message}")]
pub struct TurnError {
pub message: String,
pub codex_error_info: Option<CodexErrorInfo>,
#[serde(default)]
pub additional_details: Option<String>,
}
View on GitHub (pinned to 339751715c)
Solutions
- Read turn.error.message for the human-readable failure reason of the failed turn
- Inspect turn.error.codexErrorInfo for a machine-readable code (for example usage-limit errors) before falling back to generic failure handling
- Check turn.error.additionalDetails for supplementary context some failures attach
- Start a new turn for transient causes (network drop, provider 5xx); failed turns are terminal and app-server does not retry them
Example fix
// before
const turn = await rpc('thread/read', { threadId });
console.log(turn.error); // opaque object, fields unknown
// after
if (turn.status === 'failed' && turn.error) {
const { message, codexErrorInfo, additionalDetails } = turn.error;
reportFailure(message, codexErrorInfo ?? undefined, additionalDetails ?? undefined);
} Defensive patterns
Strategy: type-guard
Type guard
function isFailedTurn(turn: Turn): turn is Turn & { status: 'failed'; error: TurnError } {
return turn.status === 'failed' && turn.error != null;
} Try / catch
Nothing to catch: TurnError arrives as data on Turn.error, never as a thrown exception. Narrow on turn.status === 'failed' with a type guard, then destructure message / codexErrorInfo / additionalDetails.
Prevention
- Treat Turn.error as optional and only read it when Turn.status is failed
- Regenerate TS types with `just write-app-server-schema` after protocol changes instead of hand-writing TurnError shapes
- Snapshot-test TurnError deserialization; camelCase wire names (codexErrorInfo, additionalDetails) are part of the v2 contract
When it happens
Trigger: A turn fails mid-flight (model/provider request error, interrupted SSE stream, usage limit hit, invalid model or provider config) and app-server transitions Turn.status to failed, attaching TurnError. Encountered when reading a thread with a persisted failed turn via thread/read or when consuming live turn completion events.
Common situations: IDE/editor integrations that render app-server v2 threads and must show why a turn failed; resuming old rollouts where a turn died mid-stream; code that needs to branch on usage-limit failures (codexErrorInfo populated) versus transient network failures.
Related errors
- invalid network proxy attribution frame
- unsupported operation: {0}
- Unsupported platform: ${platform} (${arch})
- Unsupported target triple: ${targetTriple}
- Missing optional dependency ${platformPackage}. Reinstall Co
AI-assisted analysis of openai/codex@339751715c (2026-08-25).
Data as JSON: /api/errors/6598f64f356a5d2e.
Report an issue: GitHub.