codex_app_server_sdk/
error.rs1use crate::protocol::shared::RequestId;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use thiserror::Error;
5
6#[derive(Debug, Error)]
7pub enum ClientError {
8 #[error("not initialized: call initialize() before invoking {method}")]
9 NotInitialized { method: String },
10 #[error("connection not ready: call initialized() before invoking {method}")]
11 NotReady { method: String },
12 #[error("connection is already initialized")]
13 AlreadyInitialized,
14 #[error("request timed out after {timeout_ms}ms for method {method}")]
15 Timeout { method: String, timeout_ms: u64 },
16 #[error("transport send failed: {0}")]
17 TransportSend(String),
18 #[error("transport closed")]
19 TransportClosed,
20 #[error("invalid message from server: {0}")]
21 InvalidMessage(String),
22 #[error("serialization error: {0}")]
23 Serialization(#[from] serde_json::Error),
24 #[error("io error: {0}")]
25 Io(#[from] std::io::Error),
26 #[error("rpc error {error:?}")]
27 Rpc { error: RpcError },
28 #[error("unexpected result shape for method {method}: {source}")]
29 UnexpectedResult {
30 method: String,
31 source: serde_json::Error,
32 },
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct RpcError {
37 pub code: i64,
38 pub message: String,
39 #[serde(default, skip_serializing_if = "Option::is_none")]
40 pub data: Option<Value>,
41}
42
43#[derive(Debug)]
44pub enum IncomingClassified {
45 Response {
46 id: RequestId,
47 result: Result<Value, RpcError>,
48 },
49 Notification {
50 method: String,
51 params: Value,
52 raw: Value,
53 },
54 ServerRequest {
55 id: RequestId,
56 method: String,
57 params: Value,
58 raw: Value,
59 },
60}
61
62pub fn classify_incoming(value: Value) -> Result<IncomingClassified, ClientError> {
63 let obj = value
64 .as_object()
65 .ok_or_else(|| ClientError::InvalidMessage("expected JSON object".to_string()))?;
66
67 let id = obj
68 .get("id")
69 .map(|v| serde_json::from_value::<RequestId>(v.clone()))
70 .transpose()
71 .map_err(ClientError::Serialization)?;
72
73 let method = obj
74 .get("method")
75 .and_then(|v| v.as_str())
76 .map(|s| s.to_string());
77
78 match (id, method) {
79 (Some(id), Some(method)) => {
80 let params = obj.get("params").cloned().unwrap_or(Value::Null);
81 Ok(IncomingClassified::ServerRequest {
82 id,
83 method,
84 params,
85 raw: value,
86 })
87 }
88 (None, Some(method)) => {
89 let params = obj.get("params").cloned().unwrap_or(Value::Null);
90 Ok(IncomingClassified::Notification {
91 method,
92 params,
93 raw: value,
94 })
95 }
96 (Some(id), None) => {
97 if let Some(result) = obj.get("result") {
98 return Ok(IncomingClassified::Response {
99 id,
100 result: Ok(result.clone()),
101 });
102 }
103 if let Some(error) = obj.get("error") {
104 let parsed = serde_json::from_value::<RpcError>(error.clone())?;
105 return Ok(IncomingClassified::Response {
106 id,
107 result: Err(parsed),
108 });
109 }
110 Err(ClientError::InvalidMessage(
111 "response missing both result and error".to_string(),
112 ))
113 }
114 (None, None) => Err(ClientError::InvalidMessage(
115 "message missing method and id".to_string(),
116 )),
117 }
118}