{"record":{"id":"6598f64f356a5d2e","repo":"openai/codex","slug":"message-6598f6","errorCode":null,"errorMessage":"{message}","messagePattern":"\\{message\\}","errorType":"exception","errorClass":"TurnError","httpStatus":null,"severity":"error","filePath":"codex-rs/app-server-protocol/src/protocol/v2/thread_data.rs","lineNumber":391,"sourceCode":"}\n\n#[derive(Default, Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]\n#[serde(rename_all = \"camelCase\")]\n#[ts(export_to = \"v2/\")]\npub enum TurnItemsView {\n    /// `items` was not loaded for this turn. The field is intentionally empty.\n    NotLoaded,\n    /// `items` contains only a display summary for this turn.\n    Summary,\n    /// `items` contains every ThreadItem available from persisted app-server history for this turn.\n    #[default]\n    Full,\n}\n\n#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS, Error)]\n#[serde(rename_all = \"camelCase\")]\n#[ts(export_to = \"v2/\")]\n#[error(\"{message}\")]\npub struct TurnError {\n    pub message: String,\n    pub codex_error_info: Option<CodexErrorInfo>,\n    #[serde(default)]\n    pub additional_details: Option<String>,\n}\n","sourceCodeStart":373,"sourceCodeEnd":398,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/app-server-protocol/src/protocol/v2/thread_data.rs#L373-L398","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nconst turn = await rpc('thread/read', { threadId });\nconsole.log(turn.error); // opaque object, fields unknown\n\n// after\nif (turn.status === 'failed' && turn.error) {\n  const { message, codexErrorInfo, additionalDetails } = turn.error;\n  reportFailure(message, codexErrorInfo ?? undefined, additionalDetails ?? undefined);\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function isFailedTurn(turn: Turn): turn is Turn & { status: 'failed'; error: TurnError } {\n  return turn.status === 'failed' && turn.error != null;\n}","tryCatchPattern":"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.","preventionTips":["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"],"tags":["app-server","protocol","turn","error-payload","rust"],"backgroundTag":"api-error-object","analyzedSha":"339751715c64496cb86246bfb3935f40e309dd3d","analyzedAt":"2026-08-25T05:35:09.876Z","schemaVersion":2},"datasetVersion":"2026-08-25T06:17:31.827Z"}