mirror of
https://github.com/openai/codex.git
synced 2026-06-01 19:02:59 +00:00
[codex] Use shared app-server JSON-RPC error helpers (#21221)
## Why App-server had repeated hand-built JSON-RPC error objects for standard error shapes. Using the shared helpers keeps the common `invalid_request`, `invalid_params`, and `internal_error` construction in one place and reduces the chance of new call sites drifting from the common error payload shape. ## What changed - Replaced manual standard JSON-RPC error object creation with `internal_error(...)`, `invalid_request(...)`, and `invalid_params(...)` across app-server request processors and runtime paths. - Removed local duplicate helper definitions from search and review request handling. - Preserved existing structured `data` payloads by creating the shared helper error first and then attaching the existing metadata. - Left custom non-standard errors and raw error-code assertions intact. ## Validation - `cargo test -p codex-app-server`
This commit is contained in:
@@ -243,12 +243,9 @@ impl AccountRequestProcessor {
|
||||
}
|
||||
|
||||
fn external_auth_active_error(&self) -> JSONRPCErrorError {
|
||||
JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: "External auth is active. Use account/login/start (chatgptAuthTokens) to update it or account/logout to clear it."
|
||||
.to_string(),
|
||||
data: None,
|
||||
}
|
||||
invalid_request(
|
||||
"External auth is active. Use account/login/start (chatgptAuthTokens) to update it or account/logout to clear it.",
|
||||
)
|
||||
}
|
||||
|
||||
async fn login_api_key_common(
|
||||
@@ -263,11 +260,9 @@ impl AccountRequestProcessor {
|
||||
self.config.forced_login_method,
|
||||
Some(ForcedLoginMethod::Chatgpt)
|
||||
) {
|
||||
return Err(JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: "API key login is disabled. Use ChatGPT login instead.".to_string(),
|
||||
data: None,
|
||||
});
|
||||
return Err(invalid_request(
|
||||
"API key login is disabled. Use ChatGPT login instead.",
|
||||
));
|
||||
}
|
||||
|
||||
// Cancel any active login attempt.
|
||||
@@ -287,11 +282,7 @@ impl AccountRequestProcessor {
|
||||
self.auth_manager.reload().await;
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => Err(JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to save api key: {err}"),
|
||||
data: None,
|
||||
}),
|
||||
Err(err) => Err(internal_error(format!("failed to save api key: {err}"))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,11 +312,9 @@ impl AccountRequestProcessor {
|
||||
}
|
||||
|
||||
if matches!(config.forced_login_method, Some(ForcedLoginMethod::Api)) {
|
||||
return Err(JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: "ChatGPT login is disabled. Use API key login instead.".to_string(),
|
||||
data: None,
|
||||
});
|
||||
return Err(invalid_request(
|
||||
"ChatGPT login is disabled. Use API key login instead.",
|
||||
));
|
||||
}
|
||||
|
||||
let opts = LoginServerOptions {
|
||||
@@ -354,18 +343,10 @@ impl AccountRequestProcessor {
|
||||
|
||||
fn login_chatgpt_device_code_start_error(err: IoError) -> JSONRPCErrorError {
|
||||
let is_not_found = err.kind() == std::io::ErrorKind::NotFound;
|
||||
JSONRPCErrorError {
|
||||
code: if is_not_found {
|
||||
INVALID_REQUEST_ERROR_CODE
|
||||
} else {
|
||||
INTERNAL_ERROR_CODE
|
||||
},
|
||||
message: if is_not_found {
|
||||
err.to_string()
|
||||
} else {
|
||||
format!("failed to request device code: {err}")
|
||||
},
|
||||
data: None,
|
||||
if is_not_found {
|
||||
invalid_request(err.to_string())
|
||||
} else {
|
||||
internal_error(format!("failed to request device code: {err}"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -698,11 +679,7 @@ impl AccountRequestProcessor {
|
||||
match self.auth_manager.logout_with_revoke().await {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
return Err(JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("logout failed: {err}"),
|
||||
data: None,
|
||||
});
|
||||
return Err(internal_error(format!("logout failed: {err}")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -885,28 +862,19 @@ impl AccountRequestProcessor {
|
||||
params: SendAddCreditsNudgeEmailParams,
|
||||
) -> Result<AddCreditsNudgeEmailStatus, JSONRPCErrorError> {
|
||||
let Some(auth) = self.auth_manager.auth().await else {
|
||||
return Err(JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: "codex account authentication required to notify workspace owner"
|
||||
.to_string(),
|
||||
data: None,
|
||||
});
|
||||
return Err(invalid_request(
|
||||
"codex account authentication required to notify workspace owner",
|
||||
));
|
||||
};
|
||||
|
||||
if !auth.uses_codex_backend() {
|
||||
return Err(JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: "chatgpt authentication required to notify workspace owner".to_string(),
|
||||
data: None,
|
||||
});
|
||||
return Err(invalid_request(
|
||||
"chatgpt authentication required to notify workspace owner",
|
||||
));
|
||||
}
|
||||
|
||||
let client = BackendClient::from_auth(self.config.chatgpt_base_url.clone(), &auth)
|
||||
.map_err(|err| JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to construct backend client: {err}"),
|
||||
data: None,
|
||||
})?;
|
||||
.map_err(|err| internal_error(format!("failed to construct backend client: {err}")))?;
|
||||
|
||||
match client
|
||||
.send_add_credits_nudge_email(Self::backend_credit_type(params.credit_type))
|
||||
@@ -916,11 +884,9 @@ impl AccountRequestProcessor {
|
||||
Err(err) if err.status().is_some_and(|status| status.as_u16() == 429) => {
|
||||
Ok(AddCreditsNudgeEmailStatus::CooldownActive)
|
||||
}
|
||||
Err(err) => Err(JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to notify workspace owner: {err}"),
|
||||
data: None,
|
||||
}),
|
||||
Err(err) => Err(internal_error(format!(
|
||||
"failed to notify workspace owner: {err}"
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -941,42 +907,28 @@ impl AccountRequestProcessor {
|
||||
JSONRPCErrorError,
|
||||
> {
|
||||
let Some(auth) = self.auth_manager.auth().await else {
|
||||
return Err(JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: "codex account authentication required to read rate limits".to_string(),
|
||||
data: None,
|
||||
});
|
||||
return Err(invalid_request(
|
||||
"codex account authentication required to read rate limits",
|
||||
));
|
||||
};
|
||||
|
||||
if !auth.uses_codex_backend() {
|
||||
return Err(JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: "chatgpt authentication required to read rate limits".to_string(),
|
||||
data: None,
|
||||
});
|
||||
return Err(invalid_request(
|
||||
"chatgpt authentication required to read rate limits",
|
||||
));
|
||||
}
|
||||
|
||||
let client = BackendClient::from_auth(self.config.chatgpt_base_url.clone(), &auth)
|
||||
.map_err(|err| JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to construct backend client: {err}"),
|
||||
data: None,
|
||||
})?;
|
||||
.map_err(|err| internal_error(format!("failed to construct backend client: {err}")))?;
|
||||
|
||||
let snapshots = client
|
||||
.get_rate_limits_many()
|
||||
.await
|
||||
.map_err(|err| JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to fetch codex rate limits: {err}"),
|
||||
data: None,
|
||||
})?;
|
||||
.map_err(|err| internal_error(format!("failed to fetch codex rate limits: {err}")))?;
|
||||
if snapshots.is_empty() {
|
||||
return Err(JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: "failed to fetch codex rate limits: no snapshots returned".to_string(),
|
||||
data: None,
|
||||
});
|
||||
return Err(internal_error(
|
||||
"failed to fetch codex rate limits: no snapshots returned",
|
||||
));
|
||||
}
|
||||
|
||||
let rate_limits_by_limit_id: HashMap<String, CoreRateLimitSnapshot> = snapshots
|
||||
|
||||
@@ -231,21 +231,14 @@ impl AppsRequestProcessor {
|
||||
&self,
|
||||
thread_id: &str,
|
||||
) -> Result<(ThreadId, Arc<CodexThread>), JSONRPCErrorError> {
|
||||
let thread_id = ThreadId::from_string(thread_id).map_err(|err| JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: format!("invalid thread id: {err}"),
|
||||
data: None,
|
||||
})?;
|
||||
let thread_id = ThreadId::from_string(thread_id)
|
||||
.map_err(|err| invalid_request(format!("invalid thread id: {err}")))?;
|
||||
|
||||
let thread = self
|
||||
.thread_manager
|
||||
.get_thread(thread_id)
|
||||
.await
|
||||
.map_err(|_| JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: format!("thread not found: {thread_id}"),
|
||||
data: None,
|
||||
})?;
|
||||
.map_err(|_| invalid_request(format!("thread not found: {thread_id}")))?;
|
||||
|
||||
Ok((thread_id, thread))
|
||||
}
|
||||
@@ -257,11 +250,7 @@ impl AppsRequestProcessor {
|
||||
self.config_manager
|
||||
.load_latest_config(fallback_cwd)
|
||||
.await
|
||||
.map_err(|err| JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to reload config: {err}"),
|
||||
data: None,
|
||||
})
|
||||
.map_err(|err| internal_error(format!("failed to reload config: {err}")))
|
||||
}
|
||||
|
||||
async fn workspace_codex_plugins_enabled(
|
||||
@@ -319,11 +308,9 @@ fn paginate_apps(
|
||||
) -> Result<AppsListResponse, JSONRPCErrorError> {
|
||||
let total = connectors.len();
|
||||
if start > total {
|
||||
return Err(JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: format!("cursor {start} exceeds total apps {total}"),
|
||||
data: None,
|
||||
});
|
||||
return Err(invalid_request(format!(
|
||||
"cursor {start} exceeds total apps {total}"
|
||||
)));
|
||||
}
|
||||
|
||||
let effective_limit = limit.unwrap_or(total as u32).max(1) as usize;
|
||||
|
||||
@@ -192,11 +192,7 @@ impl CatalogRequestProcessor {
|
||||
self.config_manager
|
||||
.load_latest_config(fallback_cwd)
|
||||
.await
|
||||
.map_err(|err| JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to reload config: {err}"),
|
||||
data: None,
|
||||
})
|
||||
.map_err(|err| internal_error(format!("failed to reload config: {err}")))
|
||||
}
|
||||
|
||||
async fn workspace_codex_plugins_enabled(
|
||||
|
||||
@@ -29,9 +29,7 @@ pub(super) fn config_load_error(err: &std::io::Error) -> JSONRPCErrorError {
|
||||
data
|
||||
});
|
||||
|
||||
JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: format!("failed to load configuration: {err}"),
|
||||
data,
|
||||
}
|
||||
let mut error = invalid_request(format!("failed to load configuration: {err}"));
|
||||
error.data = data;
|
||||
error
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ use std::sync::Arc;
|
||||
|
||||
use crate::config_manager::ConfigManager;
|
||||
use crate::config_manager_service::ConfigManagerError;
|
||||
use crate::error_code::INVALID_REQUEST_ERROR_CODE;
|
||||
use crate::error_code::internal_error;
|
||||
use crate::error_code::invalid_request;
|
||||
use crate::outgoing_message::ConnectionRequestId;
|
||||
@@ -612,11 +611,9 @@ fn map_error(err: ConfigManagerError) -> JSONRPCErrorError {
|
||||
}
|
||||
|
||||
fn config_write_error(code: ConfigWriteErrorCode, message: impl Into<String>) -> JSONRPCErrorError {
|
||||
JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: message.into(),
|
||||
data: Some(json!({
|
||||
"config_write_error_code": code,
|
||||
})),
|
||||
}
|
||||
let mut error = invalid_request(message);
|
||||
error.data = Some(json!({
|
||||
"config_write_error_code": code,
|
||||
}));
|
||||
error
|
||||
}
|
||||
|
||||
@@ -132,10 +132,6 @@ impl MarketplaceRequestProcessor {
|
||||
self.config_manager
|
||||
.load_latest_config(fallback_cwd)
|
||||
.await
|
||||
.map_err(|err| JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to reload config: {err}"),
|
||||
data: None,
|
||||
})
|
||||
.map_err(|err| internal_error(format!("failed to reload config: {err}")))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,32 +89,21 @@ impl McpRequestProcessor {
|
||||
self.config_manager
|
||||
.load_latest_config(fallback_cwd)
|
||||
.await
|
||||
.map_err(|err| JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to reload config: {err}"),
|
||||
data: None,
|
||||
})
|
||||
.map_err(|err| internal_error(format!("failed to reload config: {err}")))
|
||||
}
|
||||
|
||||
async fn load_thread(
|
||||
&self,
|
||||
thread_id: &str,
|
||||
) -> Result<(ThreadId, Arc<CodexThread>), JSONRPCErrorError> {
|
||||
let thread_id = ThreadId::from_string(thread_id).map_err(|err| JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: format!("invalid thread id: {err}"),
|
||||
data: None,
|
||||
})?;
|
||||
let thread_id = ThreadId::from_string(thread_id)
|
||||
.map_err(|err| invalid_request(format!("invalid thread id: {err}")))?;
|
||||
|
||||
let thread = self
|
||||
.thread_manager
|
||||
.get_thread(thread_id)
|
||||
.await
|
||||
.map_err(|_| JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: format!("thread not found: {thread_id}"),
|
||||
data: None,
|
||||
})?;
|
||||
.map_err(|_| invalid_request(format!("thread not found: {thread_id}")))?;
|
||||
|
||||
Ok((thread_id, thread))
|
||||
}
|
||||
@@ -130,11 +119,9 @@ impl McpRequestProcessor {
|
||||
let mcp_servers = match serde_json::to_value(configured_servers) {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
return Err(JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to serialize MCP servers: {err}"),
|
||||
data: None,
|
||||
});
|
||||
return Err(internal_error(format!(
|
||||
"failed to serialize MCP servers: {err}"
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -142,13 +129,9 @@ impl McpRequestProcessor {
|
||||
match serde_json::to_value(config.mcp_oauth_credentials_store_mode) {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
return Err(JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!(
|
||||
"failed to serialize MCP OAuth credentials store mode: {err}"
|
||||
),
|
||||
data: None,
|
||||
});
|
||||
return Err(internal_error(format!(
|
||||
"failed to serialize MCP OAuth credentials store mode: {err}"
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -219,11 +219,7 @@ impl PluginRequestProcessor {
|
||||
self.config_manager
|
||||
.load_latest_config(fallback_cwd)
|
||||
.await
|
||||
.map_err(|err| JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to reload config: {err}"),
|
||||
data: None,
|
||||
})
|
||||
.map_err(|err| internal_error(format!("failed to reload config: {err}")))
|
||||
}
|
||||
|
||||
async fn workspace_codex_plugins_enabled(
|
||||
@@ -1307,16 +1303,17 @@ fn remote_plugin_catalog_error_to_jsonrpc(
|
||||
err: RemotePluginCatalogError,
|
||||
context: &str,
|
||||
) -> JSONRPCErrorError {
|
||||
let code = match &err {
|
||||
let message = format!("{context}: {err}");
|
||||
match &err {
|
||||
RemotePluginCatalogError::AuthRequired | RemotePluginCatalogError::UnsupportedAuthMode => {
|
||||
INVALID_REQUEST_ERROR_CODE
|
||||
invalid_request(message)
|
||||
}
|
||||
RemotePluginCatalogError::UnexpectedStatus { status, .. } if status.as_u16() == 404 => {
|
||||
INVALID_REQUEST_ERROR_CODE
|
||||
invalid_request(message)
|
||||
}
|
||||
RemotePluginCatalogError::InvalidPluginPath { .. }
|
||||
| RemotePluginCatalogError::ArchiveTooLarge { .. }
|
||||
| RemotePluginCatalogError::UnknownMarketplace { .. } => INVALID_REQUEST_ERROR_CODE,
|
||||
| RemotePluginCatalogError::UnknownMarketplace { .. } => invalid_request(message),
|
||||
RemotePluginCatalogError::AuthToken(_)
|
||||
| RemotePluginCatalogError::Request { .. }
|
||||
| RemotePluginCatalogError::UnexpectedStatus { .. }
|
||||
@@ -1330,12 +1327,7 @@ fn remote_plugin_catalog_error_to_jsonrpc(
|
||||
| RemotePluginCatalogError::ArchiveJoin(_)
|
||||
| RemotePluginCatalogError::MissingUploadEtag
|
||||
| RemotePluginCatalogError::UnexpectedResponse(_)
|
||||
| RemotePluginCatalogError::CacheRemove(_) => INTERNAL_ERROR_CODE,
|
||||
};
|
||||
JSONRPCErrorError {
|
||||
code,
|
||||
message: format!("{context}: {err}"),
|
||||
data: None,
|
||||
| RemotePluginCatalogError::CacheRemove(_) => internal_error(message),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use crate::error_code::INTERNAL_ERROR_CODE;
|
||||
use crate::error_code::INVALID_REQUEST_ERROR_CODE;
|
||||
use crate::error_code::internal_error;
|
||||
use crate::error_code::invalid_request;
|
||||
use crate::fuzzy_file_search::FuzzyFileSearchSession;
|
||||
use crate::fuzzy_file_search::run_fuzzy_file_search;
|
||||
use crate::fuzzy_file_search::start_fuzzy_file_search_session;
|
||||
@@ -132,19 +132,3 @@ impl SearchRequestProcessor {
|
||||
Ok(FuzzyFileSearchSessionStopResponse {})
|
||||
}
|
||||
}
|
||||
|
||||
fn invalid_request(message: impl Into<String>) -> JSONRPCErrorError {
|
||||
JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: message.into(),
|
||||
data: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn internal_error(message: impl Into<String>) -> JSONRPCErrorError {
|
||||
JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: message.into(),
|
||||
data: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,23 +147,17 @@ pub(super) async fn ensure_conversation_listener(
|
||||
{
|
||||
Ok(conv) => conv,
|
||||
Err(_) => {
|
||||
return Err(JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: format!("thread not found: {conversation_id}"),
|
||||
data: None,
|
||||
});
|
||||
return Err(invalid_request(format!(
|
||||
"thread not found: {conversation_id}"
|
||||
)));
|
||||
}
|
||||
};
|
||||
let thread_state = {
|
||||
let pending_thread_unloads = listener_task_context.pending_thread_unloads.lock().await;
|
||||
if pending_thread_unloads.contains(&conversation_id) {
|
||||
return Err(JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: format!(
|
||||
"thread {conversation_id} is closing; retry after the thread is closed"
|
||||
),
|
||||
data: None,
|
||||
});
|
||||
return Err(invalid_request(format!(
|
||||
"thread {conversation_id} is closing; retry after the thread is closed"
|
||||
)));
|
||||
}
|
||||
let Some(thread_state) = listener_task_context
|
||||
.thread_state_manager
|
||||
@@ -229,13 +223,9 @@ pub(super) async fn ensure_listener_task_running(
|
||||
)
|
||||
.await
|
||||
else {
|
||||
return Err(JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: format!(
|
||||
"thread {conversation_id} is closing; retry after the thread is closed"
|
||||
),
|
||||
data: None,
|
||||
});
|
||||
return Err(invalid_request(format!(
|
||||
"thread {conversation_id} is closing; retry after the thread is closed"
|
||||
)));
|
||||
};
|
||||
let (mut listener_command_rx, listener_generation) = {
|
||||
let mut thread_state = thread_state.lock().await;
|
||||
|
||||
@@ -163,10 +163,8 @@ fn normalize_thread_list_cwd_filters(
|
||||
for cwd in cwds {
|
||||
let cwd = AbsolutePathBuf::relative_to_current_dir(cwd.as_str())
|
||||
.map(AbsolutePathBuf::into_path_buf)
|
||||
.map_err(|err| JSONRPCErrorError {
|
||||
code: INVALID_PARAMS_ERROR_CODE,
|
||||
message: format!("invalid thread/list cwd filter `{cwd}`: {err}"),
|
||||
data: None,
|
||||
.map_err(|err| {
|
||||
invalid_params(format!("invalid thread/list cwd filter `{cwd}`: {err}"))
|
||||
})?;
|
||||
normalized_cwds.push(cwd);
|
||||
}
|
||||
@@ -565,21 +563,14 @@ impl ThreadRequestProcessor {
|
||||
thread_id: &str,
|
||||
) -> Result<(ThreadId, Arc<CodexThread>), JSONRPCErrorError> {
|
||||
// Resolve the core conversation handle from a v2 thread id string.
|
||||
let thread_id = ThreadId::from_string(thread_id).map_err(|err| JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: format!("invalid thread id: {err}"),
|
||||
data: None,
|
||||
})?;
|
||||
let thread_id = ThreadId::from_string(thread_id)
|
||||
.map_err(|err| invalid_request(format!("invalid thread id: {err}")))?;
|
||||
|
||||
let thread = self
|
||||
.thread_manager
|
||||
.get_thread(thread_id)
|
||||
.await
|
||||
.map_err(|_| JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: format!("thread not found: {thread_id}"),
|
||||
data: None,
|
||||
})?;
|
||||
.map_err(|_| invalid_request(format!("thread not found: {thread_id}")))?;
|
||||
|
||||
Ok((thread_id, thread))
|
||||
}
|
||||
@@ -602,11 +593,7 @@ impl ThreadRequestProcessor {
|
||||
thread
|
||||
.set_app_server_client_info(app_server_client_name, app_server_client_version)
|
||||
.await
|
||||
.map_err(|err| JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to set app server client info: {err}"),
|
||||
data: None,
|
||||
})
|
||||
.map_err(|err| internal_error(format!("failed to set app server client info: {err}")))
|
||||
}
|
||||
|
||||
async fn finalize_thread_teardown(&self, thread_id: ThreadId) {
|
||||
@@ -1548,10 +1535,8 @@ impl ThreadRequestProcessor {
|
||||
.and_then(|stored_thread| {
|
||||
summary_from_stored_thread(stored_thread, fallback_provider.as_str())
|
||||
.map(|summary| summary_to_thread(summary, &self.config.cwd))
|
||||
.ok_or_else(|| JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to read unarchived thread {thread_id}"),
|
||||
data: None,
|
||||
.ok_or_else(|| {
|
||||
internal_error(format!("failed to read unarchived thread {thread_id}"))
|
||||
})
|
||||
})?;
|
||||
|
||||
@@ -3259,11 +3244,9 @@ fn paginate_thread_turns(
|
||||
.as_ref()
|
||||
.and_then(|anchor| turns.iter().position(|turn| turn.id == anchor.turn_id));
|
||||
if anchor.is_some() && anchor_index.is_none() {
|
||||
return Err(JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: "invalid cursor: anchor turn is no longer present".to_string(),
|
||||
data: None,
|
||||
});
|
||||
return Err(invalid_request(
|
||||
"invalid cursor: anchor turn is no longer present",
|
||||
));
|
||||
}
|
||||
|
||||
let mut keyed_turns: Vec<_> = turns.into_iter().enumerate().collect();
|
||||
@@ -3324,19 +3307,11 @@ fn serialize_thread_turns_cursor(
|
||||
turn_id: turn_id.to_string(),
|
||||
include_anchor,
|
||||
})
|
||||
.map_err(|err| JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to serialize cursor: {err}"),
|
||||
data: None,
|
||||
})
|
||||
.map_err(|err| internal_error(format!("failed to serialize cursor: {err}")))
|
||||
}
|
||||
|
||||
fn parse_thread_turns_cursor(cursor: &str) -> Result<ThreadTurnsCursor, JSONRPCErrorError> {
|
||||
serde_json::from_str(cursor).map_err(|_| JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: format!("invalid cursor: {cursor}"),
|
||||
data: None,
|
||||
})
|
||||
serde_json::from_str(cursor).map_err(|_| invalid_request(format!("invalid cursor: {cursor}")))
|
||||
}
|
||||
|
||||
fn reconstruct_thread_turns_for_turns_list(
|
||||
@@ -3387,36 +3362,18 @@ fn thread_read_view_error(err: ThreadReadViewError) -> JSONRPCErrorError {
|
||||
|
||||
fn thread_store_list_error(err: ThreadStoreError) -> JSONRPCErrorError {
|
||||
match err {
|
||||
ThreadStoreError::InvalidRequest { message } => JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message,
|
||||
data: None,
|
||||
},
|
||||
err => JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to list threads: {err}"),
|
||||
data: None,
|
||||
},
|
||||
ThreadStoreError::InvalidRequest { message } => invalid_request(message),
|
||||
err => internal_error(format!("failed to list threads: {err}")),
|
||||
}
|
||||
}
|
||||
|
||||
fn thread_store_resume_read_error(err: ThreadStoreError) -> JSONRPCErrorError {
|
||||
match err {
|
||||
ThreadStoreError::InvalidRequest { message } => JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message,
|
||||
data: None,
|
||||
},
|
||||
ThreadStoreError::ThreadNotFound { thread_id } => JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: format!("no rollout found for thread id {thread_id}"),
|
||||
data: None,
|
||||
},
|
||||
err => JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to read thread: {err}"),
|
||||
data: None,
|
||||
},
|
||||
ThreadStoreError::InvalidRequest { message } => invalid_request(message),
|
||||
ThreadStoreError::ThreadNotFound { thread_id } => {
|
||||
invalid_request(format!("no rollout found for thread id {thread_id}"))
|
||||
}
|
||||
err => internal_error(format!("failed to read thread: {err}")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3479,25 +3436,17 @@ fn conversation_summary_thread_id_read_error(
|
||||
ThreadStoreError::ThreadNotFound { thread_id } if thread_id == conversation_id => {
|
||||
conversation_summary_not_found_error(conversation_id)
|
||||
}
|
||||
ThreadStoreError::InvalidRequest { message } => JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message,
|
||||
data: None,
|
||||
},
|
||||
err => JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to load conversation summary for {conversation_id}: {err}"),
|
||||
data: None,
|
||||
},
|
||||
ThreadStoreError::InvalidRequest { message } => invalid_request(message),
|
||||
err => internal_error(format!(
|
||||
"failed to load conversation summary for {conversation_id}: {err}"
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn conversation_summary_not_found_error(conversation_id: ThreadId) -> JSONRPCErrorError {
|
||||
JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: format!("no rollout found for conversation id {conversation_id}"),
|
||||
data: None,
|
||||
}
|
||||
invalid_request(format!(
|
||||
"no rollout found for conversation id {conversation_id}"
|
||||
))
|
||||
}
|
||||
|
||||
fn conversation_summary_rollout_path_read_error(
|
||||
@@ -3505,55 +3454,29 @@ fn conversation_summary_rollout_path_read_error(
|
||||
err: ThreadStoreError,
|
||||
) -> JSONRPCErrorError {
|
||||
match err {
|
||||
ThreadStoreError::InvalidRequest { message } => JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message,
|
||||
data: None,
|
||||
},
|
||||
err => JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!(
|
||||
"failed to load conversation summary from {}: {}",
|
||||
path.display(),
|
||||
err
|
||||
),
|
||||
data: None,
|
||||
},
|
||||
ThreadStoreError::InvalidRequest { message } => invalid_request(message),
|
||||
err => internal_error(format!(
|
||||
"failed to load conversation summary from {}: {}",
|
||||
path.display(),
|
||||
err
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn thread_store_write_error(operation: &str, err: ThreadStoreError) -> JSONRPCErrorError {
|
||||
match err {
|
||||
ThreadStoreError::ThreadNotFound { thread_id } => JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: format!("thread not found: {thread_id}"),
|
||||
data: None,
|
||||
},
|
||||
ThreadStoreError::InvalidRequest { message } => JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message,
|
||||
data: None,
|
||||
},
|
||||
err => JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to {operation}: {err}"),
|
||||
data: None,
|
||||
},
|
||||
ThreadStoreError::ThreadNotFound { thread_id } => {
|
||||
invalid_request(format!("thread not found: {thread_id}"))
|
||||
}
|
||||
ThreadStoreError::InvalidRequest { message } => invalid_request(message),
|
||||
err => internal_error(format!("failed to {operation}: {err}")),
|
||||
}
|
||||
}
|
||||
|
||||
fn thread_store_archive_error(operation: &str, err: ThreadStoreError) -> JSONRPCErrorError {
|
||||
match err {
|
||||
ThreadStoreError::InvalidRequest { message } => JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message,
|
||||
data: None,
|
||||
},
|
||||
err => JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to {operation} thread: {err}"),
|
||||
data: None,
|
||||
},
|
||||
ThreadStoreError::InvalidRequest { message } => invalid_request(message),
|
||||
err => internal_error(format!("failed to {operation} thread: {err}")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -171,21 +171,14 @@ impl TurnRequestProcessor {
|
||||
thread_id: &str,
|
||||
) -> Result<(ThreadId, Arc<CodexThread>), JSONRPCErrorError> {
|
||||
// Resolve the core conversation handle from a v2 thread id string.
|
||||
let thread_id = ThreadId::from_string(thread_id).map_err(|err| JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: format!("invalid thread id: {err}"),
|
||||
data: None,
|
||||
})?;
|
||||
let thread_id = ThreadId::from_string(thread_id)
|
||||
.map_err(|err| invalid_request(format!("invalid thread id: {err}")))?;
|
||||
|
||||
let thread = self
|
||||
.thread_manager
|
||||
.get_thread(thread_id)
|
||||
.await
|
||||
.map_err(|_| JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: format!("thread not found: {thread_id}"),
|
||||
data: None,
|
||||
})?;
|
||||
.map_err(|_| invalid_request(format!("thread not found: {thread_id}")))?;
|
||||
|
||||
Ok((thread_id, thread))
|
||||
}
|
||||
@@ -209,14 +202,6 @@ impl TurnRequestProcessor {
|
||||
fn review_request_from_target(
|
||||
target: ApiReviewTarget,
|
||||
) -> Result<(ReviewRequest, String), JSONRPCErrorError> {
|
||||
fn invalid_request(message: String) -> JSONRPCErrorError {
|
||||
JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message,
|
||||
data: None,
|
||||
}
|
||||
}
|
||||
|
||||
let cleaned_target = match target {
|
||||
ApiReviewTarget::UncommittedChanges => ApiReviewTarget::UncommittedChanges,
|
||||
ApiReviewTarget::BaseBranch { branch } => {
|
||||
@@ -305,17 +290,15 @@ impl TurnRequestProcessor {
|
||||
}
|
||||
|
||||
fn input_too_large_error(actual_chars: usize) -> JSONRPCErrorError {
|
||||
JSONRPCErrorError {
|
||||
code: INVALID_PARAMS_ERROR_CODE,
|
||||
message: format!(
|
||||
"Input exceeds the maximum length of {MAX_USER_INPUT_TEXT_CHARS} characters."
|
||||
),
|
||||
data: Some(serde_json::json!({
|
||||
"input_error_code": INPUT_TOO_LARGE_ERROR_CODE,
|
||||
"max_chars": MAX_USER_INPUT_TEXT_CHARS,
|
||||
"actual_chars": actual_chars,
|
||||
})),
|
||||
}
|
||||
let mut error = invalid_params(format!(
|
||||
"Input exceeds the maximum length of {MAX_USER_INPUT_TEXT_CHARS} characters."
|
||||
));
|
||||
error.data = Some(serde_json::json!({
|
||||
"input_error_code": INPUT_TOO_LARGE_ERROR_CODE,
|
||||
"max_chars": MAX_USER_INPUT_TEXT_CHARS,
|
||||
"actual_chars": actual_chars,
|
||||
}));
|
||||
error
|
||||
}
|
||||
|
||||
fn validate_v2_input_limit(items: &[V2UserInput]) -> Result<(), JSONRPCErrorError> {
|
||||
@@ -564,11 +547,7 @@ impl TurnRequestProcessor {
|
||||
thread
|
||||
.set_app_server_client_info(app_server_client_name, app_server_client_version)
|
||||
.await
|
||||
.map_err(|err| JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to set app server client info: {err}"),
|
||||
data: None,
|
||||
})
|
||||
.map_err(|err| internal_error(format!("failed to set app server client info: {err}")))
|
||||
}
|
||||
|
||||
async fn turn_steer_inner(
|
||||
@@ -612,9 +591,8 @@ impl TurnRequestProcessor {
|
||||
)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
let (code, message, data, error_type) = match err {
|
||||
let (message, data, error_type) = match err {
|
||||
SteerInputError::NoActiveTurn(_) => (
|
||||
INVALID_REQUEST_ERROR_CODE,
|
||||
"no active turn to steer".to_string(),
|
||||
None,
|
||||
Some(AnalyticsJsonRpcError::TurnSteer(
|
||||
@@ -622,7 +600,6 @@ impl TurnRequestProcessor {
|
||||
)),
|
||||
),
|
||||
SteerInputError::ExpectedTurnMismatch { expected, actual } => (
|
||||
INVALID_REQUEST_ERROR_CODE,
|
||||
format!("expected active turn id `{expected}` but found `{actual}`"),
|
||||
None,
|
||||
Some(AnalyticsJsonRpcError::TurnSteer(
|
||||
@@ -658,24 +635,19 @@ impl TurnRequestProcessor {
|
||||
}
|
||||
};
|
||||
(
|
||||
INVALID_REQUEST_ERROR_CODE,
|
||||
message,
|
||||
data,
|
||||
Some(AnalyticsJsonRpcError::TurnSteer(turn_steer_error)),
|
||||
)
|
||||
}
|
||||
SteerInputError::EmptyInput => (
|
||||
INVALID_REQUEST_ERROR_CODE,
|
||||
"input must not be empty".to_string(),
|
||||
None,
|
||||
Some(AnalyticsJsonRpcError::Input(InputError::Empty)),
|
||||
),
|
||||
};
|
||||
let error = JSONRPCErrorError {
|
||||
code,
|
||||
message,
|
||||
data,
|
||||
};
|
||||
let mut error = invalid_request(message);
|
||||
error.data = data;
|
||||
self.track_error_response(request_id, &error, error_type);
|
||||
error
|
||||
})?;
|
||||
|
||||
Reference in New Issue
Block a user