diff --git a/codex-rs/app-server/src/request_processors/plugins.rs b/codex-rs/app-server/src/request_processors/plugins.rs index f0d755852688..ed8495d60210 100644 --- a/codex-rs/app-server/src/request_processors/plugins.rs +++ b/codex-rs/app-server/src/request_processors/plugins.rs @@ -1548,6 +1548,7 @@ impl PluginRequestProcessor { &remote_marketplace_name, /*plugin_id*/ None, error_type, + /*sub_error_type*/ None, err.to_string(), ); remote_plugin_catalog_error_to_jsonrpc( @@ -1591,11 +1592,13 @@ impl PluginRequestProcessor { ) .map_err(|err| { let error_type = remote_plugin_bundle_install_error_type(&err); + let sub_error_type = err.sub_error_type(); self.track_plugin_install_failed_for_remote_plugin( &remote_plugin_id, &actual_remote_marketplace_name, Some(&resolved_plugin_id), error_type, + sub_error_type, err.to_string(), ); remote_plugin_bundle_install_error_to_jsonrpc(err) @@ -1608,11 +1611,13 @@ impl PluginRequestProcessor { .await .map_err(|err| { let error_type = remote_plugin_bundle_install_error_type(&err); + let sub_error_type = err.sub_error_type(); self.track_plugin_install_failed_for_remote_plugin( &remote_plugin_id, &actual_remote_marketplace_name, Some(&resolved_plugin_id), error_type, + sub_error_type, err.to_string(), ); remote_plugin_bundle_install_error_to_jsonrpc(err) @@ -1635,6 +1640,7 @@ impl PluginRequestProcessor { &actual_remote_marketplace_name, Some(&result.plugin_id), error_type, + /*sub_error_type*/ None, err.to_string(), ); remote_plugin_catalog_error_to_jsonrpc(err, "install remote plugin") @@ -1732,12 +1738,14 @@ impl PluginRequestProcessor { marketplace_name: &str, plugin_id: Option<&PluginId>, error_type: &'static str, + sub_error_type: Option, error_message: String, ) { tracing::warn!( remote_plugin_id = %remote_plugin_id, marketplace_name = %marketplace_name, error_type = %error_type, + sub_error_type = sub_error_type.as_deref(), error = %error_message, "remote plugin install failed" ); diff --git a/codex-rs/core-plugins/src/manager.rs b/codex-rs/core-plugins/src/manager.rs index 4d215b61d455..6fac0fafe989 100644 --- a/codex-rs/core-plugins/src/manager.rs +++ b/codex-rs/core-plugins/src/manager.rs @@ -1283,6 +1283,7 @@ impl PluginsManager { self.track_plugin_install_failed( &plugin_id, plugin_install_error_type(&err), + plugin_install_sub_error_type(&err), err.to_string(), ); Err(err) @@ -1345,6 +1346,7 @@ impl PluginsManager { self.track_plugin_install_failed( &resolved.plugin_id, plugin_install_error_type(&err), + plugin_install_sub_error_type(&err), err.to_string(), ); return Err(err); @@ -1356,6 +1358,7 @@ impl PluginsManager { self.track_plugin_install_failed( &plugin_id, plugin_install_error_type(&err), + plugin_install_sub_error_type(&err), err.to_string(), ); Err(err) @@ -1383,6 +1386,7 @@ impl PluginsManager { self.track_plugin_install_failed( &plugin_id, marketplace_error_type(err), + /*sub_error_type*/ None, err.to_string(), ); } else { @@ -1398,11 +1402,13 @@ impl PluginsManager { &self, plugin_id: &PluginId, error_type: &'static str, + sub_error_type: Option, error_message: String, ) { tracing::warn!( plugin_id = %plugin_id.as_key(), error_type = %error_type, + sub_error_type = sub_error_type.as_deref(), error = %error_message, "plugin install failed" ); @@ -2674,6 +2680,16 @@ fn plugin_install_error_type(err: &PluginInstallError) -> &'static str { } } +fn plugin_install_sub_error_type(err: &PluginInstallError) -> Option { + match err { + PluginInstallError::Store(err) => err.sub_error_type(), + PluginInstallError::Marketplace(_) + | PluginInstallError::Remote(_) + | PluginInstallError::Config(_) + | PluginInstallError::Join(_) => None, + } +} + fn marketplace_error_type(err: &MarketplaceError) -> &'static str { match err { MarketplaceError::Io { .. } => "marketplace_io", diff --git a/codex-rs/core-plugins/src/remote_bundle.rs b/codex-rs/core-plugins/src/remote_bundle.rs index 66592dc363ef..7a302f2f364a 100644 --- a/codex-rs/core-plugins/src/remote_bundle.rs +++ b/codex-rs/core-plugins/src/remote_bundle.rs @@ -4,6 +4,7 @@ use crate::remote::REMOTE_GLOBAL_MARKETPLACE_NAME; use crate::store::PluginInstallResult; use crate::store::PluginStore; use crate::store::PluginStoreError; +use crate::store::error_context_sub_error_type; use crate::store::validate_plugin_version_segment; use codex_login::default_client::build_reqwest_client; use codex_plugin::PluginId; @@ -132,6 +133,26 @@ impl RemotePluginBundleInstallError { fn io(context: &'static str, source: io::Error) -> Self { Self::Io { context, source } } + + pub fn sub_error_type(&self) -> Option { + match self { + Self::Io { context, .. } => Some(error_context_sub_error_type(context)), + Self::Store(err) => err.sub_error_type(), + Self::MissingReleaseVersion { .. } + | Self::InvalidReleaseVersion { .. } + | Self::MissingBundleDownloadUrl { .. } + | Self::InvalidBundleDownloadUrl { .. } + | Self::UnsupportedBundleDownloadUrlScheme { .. } + | Self::InvalidPluginId { .. } + | Self::DownloadRequest { .. } + | Self::DownloadStatus { .. } + | Self::DownloadBody { .. } + | Self::DownloadTooLarge { .. } + | Self::UnsupportedBundleDownloadFinalUrl { .. } + | Self::ExtractedBundleTooLarge { .. } + | Self::InvalidBundle(_) => None, + } + } } pub fn validate_remote_plugin_bundle( diff --git a/codex-rs/core-plugins/src/store.rs b/codex-rs/core-plugins/src/store.rs index bb4929dedbfe..819f9731be05 100644 --- a/codex-rs/core-plugins/src/store.rs +++ b/codex-rs/core-plugins/src/store.rs @@ -365,6 +365,17 @@ impl PluginStoreError { fn io(context: &'static str, source: io::Error) -> Self { Self::Io { context, source } } + + pub(crate) fn sub_error_type(&self) -> Option { + match self { + Self::Io { context, .. } => Some(error_context_sub_error_type(context)), + Self::Invalid(_) => None, + } + } +} + +pub(crate) fn error_context_sub_error_type(context: &str) -> String { + context.to_ascii_lowercase().replace(' ', "_") } pub fn plugin_version_for_source(source_path: &Path) -> Result {