From 3fae30dd852c64bd0d8a5523cdd4d7147da99103 Mon Sep 17 00:00:00 2001 From: charlesgong-openai Date: Tue, 14 Jul 2026 22:13:09 +0000 Subject: [PATCH] Preserve plugin install failure subtypes during imports (#33182) ## What changed - Add `subErrorType` to external agent config import failures and carry it through progress and completion notifications, persisted import histories, and analytics. - Expose plugin store failure subtypes from `PluginInstallError` so manual installs and imported plugins report details such as `failed_to_copy_plugin_file` while failures without a subtype remain `null`. - Keep existing persisted import records compatible by defaulting the new field when it is absent. ## Testing - Cover store I/O subtype extraction, analytics serialization, persisted import histories, and migration notification fixtures. GitOrigin-RevId: ec1a85623fc826cbd7cfe9f55c2dd2bc86665937 --- .../analytics/src/analytics_client_tests.rs | 40 ++++++++++++------- codex-rs/analytics/src/client.rs | 2 + codex-rs/analytics/src/events.rs | 2 + codex-rs/analytics/src/facts.rs | 2 + codex-rs/analytics/src/reducer.rs | 3 ++ .../schema/json/ServerNotification.json | 6 +++ .../codex_app_server_protocol.schemas.json | 6 +++ .../codex_app_server_protocol.v2.schemas.json | 6 +++ ...gentConfigImportCompletedNotification.json | 6 +++ ...gentConfigImportHistoriesReadResponse.json | 6 +++ ...AgentConfigImportProgressNotification.json | 6 +++ ...xternalAgentConfigImportItemTypeFailure.ts | 2 +- .../src/protocol/v2/config.rs | 1 + .../src/external_agent_migration/processor.rs | 4 ++ .../src/external_agent_migration/service.rs | 6 +++ .../external_agent_migration/service_tests.rs | 1 + .../src/request_processors/plugins.rs | 1 + codex-rs/core-plugins/src/manager.rs | 24 +++++------ codex-rs/core-plugins/src/manager_tests.rs | 13 ++++++ .../runtime/external_agent_config_imports.rs | 2 + .../external_agent_config_imports_tests.rs | 3 ++ ...ernal_agent_config_migration_flow_tests.rs | 1 + 22 files changed, 114 insertions(+), 29 deletions(-) diff --git a/codex-rs/analytics/src/analytics_client_tests.rs b/codex-rs/analytics/src/analytics_client_tests.rs index 57e769369c..c95bca57e5 100644 --- a/codex-rs/analytics/src/analytics_client_tests.rs +++ b/codex-rs/analytics/src/analytics_client_tests.rs @@ -3251,6 +3251,7 @@ fn plugin_install_failed_event_serializes_expected_shape() { plugin: codex_plugin_metadata(sample_plugin_metadata()), source: PluginInstallSource::Manual, error_type: "store_io".to_string(), + sub_error_type: Some("failed_to_copy_plugin_file".to_string()), }, }); @@ -3270,7 +3271,8 @@ fn plugin_install_failed_event_serializes_expected_shape() { "connector_ids": ["calendar", "drive"], "product_client_id": originator().value, "source": "manual", - "error_type": "store_io" + "error_type": "store_io", + "sub_error_type": "failed_to_copy_plugin_file" } }) ); @@ -3703,6 +3705,7 @@ async fn reducer_ingests_plugin_install_failed_fact() { plugin: sample_plugin_metadata(), source: PluginInstallSource::ExternalAgentMigration, error_type: "invalid_plugin".to_string(), + sub_error_type: Some("failed_to_copy_plugin_file".to_string()), }, )), &mut events, @@ -3724,7 +3727,8 @@ async fn reducer_ingests_plugin_install_failed_fact() { "connector_ids": ["calendar", "drive"], "product_client_id": originator().value, "source": "external_agent_migration", - "error_type": "invalid_plugin" + "error_type": "invalid_plugin", + "sub_error_type": "failed_to_copy_plugin_file" } }]) ); @@ -3747,6 +3751,7 @@ async fn reducer_ingests_plugin_install_failed_fact_without_detail() { plugin, source: PluginInstallSource::Manual, error_type: "remote_catalog_unexpected_status".to_string(), + sub_error_type: None, }, )), &mut events, @@ -3768,7 +3773,8 @@ async fn reducer_ingests_plugin_install_failed_fact_without_detail() { "connector_ids": null, "product_client_id": originator().value, "source": "manual", - "error_type": "remote_catalog_unexpected_status" + "error_type": "remote_catalog_unexpected_status", + "sub_error_type": null } }]) ); @@ -3819,9 +3825,10 @@ fn external_agent_config_import_failure_event_serializes_expected_shape() { event_params: CodexOnboardingExternalAgentImportFailureMetadata { import_id: "import-1".to_string(), source: "app_server".to_string(), - item_type: "SESSIONS".to_string(), - failure_stage: "session_missing".to_string(), - error_type: "session_missing".to_string(), + item_type: "PLUGINS".to_string(), + failure_stage: "plugin_import".to_string(), + error_type: "plugin_import".to_string(), + sub_error_type: Some("failed_to_copy_plugin_file".to_string()), product_client_id: Some(originator().value), }, }, @@ -3836,9 +3843,10 @@ fn external_agent_config_import_failure_event_serializes_expected_shape() { "event_params": { "import_id": "import-1", "source": "app_server", - "type": "SESSIONS", - "failure_stage": "session_missing", - "error_type": "session_missing", + "type": "PLUGINS", + "failure_stage": "plugin_import", + "error_type": "plugin_import", + "sub_error_type": "failed_to_copy_plugin_file", "product_client_id": originator().value, } }) @@ -3856,9 +3864,10 @@ async fn reducer_ingests_external_agent_config_import_failure_fact() { ExternalAgentConfigImportFailureInput { import_id: "import-1".to_string(), source: "app_server".to_string(), - item_type: "SESSIONS".to_string(), - failure_stage: "session_missing".to_string(), - error_type: "session_missing".to_string(), + item_type: "PLUGINS".to_string(), + failure_stage: "plugin_import".to_string(), + error_type: "plugin_import".to_string(), + sub_error_type: Some("failed_to_copy_plugin_file".to_string()), }, )), &mut events, @@ -3873,9 +3882,10 @@ async fn reducer_ingests_external_agent_config_import_failure_fact() { "event_params": { "import_id": "import-1", "source": "app_server", - "type": "SESSIONS", - "failure_stage": "session_missing", - "error_type": "session_missing", + "type": "PLUGINS", + "failure_stage": "plugin_import", + "error_type": "plugin_import", + "sub_error_type": "failed_to_copy_plugin_file", "product_client_id": originator().value, } }]) diff --git a/codex-rs/analytics/src/client.rs b/codex-rs/analytics/src/client.rs index 7ee5737c4e..853a15ce28 100644 --- a/codex-rs/analytics/src/client.rs +++ b/codex-rs/analytics/src/client.rs @@ -376,12 +376,14 @@ impl AnalyticsEventsClient { plugin: PluginTelemetryMetadata, source: PluginInstallSource, error_type: String, + sub_error_type: Option, ) { self.record_fact(AnalyticsFact::Custom( CustomAnalyticsFact::PluginInstallFailed(PluginInstallFailedInput { plugin, source, error_type, + sub_error_type, }), )); } diff --git a/codex-rs/analytics/src/events.rs b/codex-rs/analytics/src/events.rs index 6cbdbe0e19..bc4742f2e9 100644 --- a/codex-rs/analytics/src/events.rs +++ b/codex-rs/analytics/src/events.rs @@ -1004,6 +1004,7 @@ pub(crate) struct CodexPluginInstallFailedMetadata { pub(crate) plugin: CodexPluginMetadata, pub(crate) source: crate::facts::PluginInstallSource, pub(crate) error_type: String, + pub(crate) sub_error_type: Option, } #[derive(Serialize)] @@ -1037,6 +1038,7 @@ pub(crate) struct CodexOnboardingExternalAgentImportFailureMetadata { pub(crate) item_type: String, pub(crate) failure_stage: String, pub(crate) error_type: String, + pub(crate) sub_error_type: Option, pub(crate) product_client_id: Option, } diff --git a/codex-rs/analytics/src/facts.rs b/codex-rs/analytics/src/facts.rs index 1f3333b8bc..4f99810e8e 100644 --- a/codex-rs/analytics/src/facts.rs +++ b/codex-rs/analytics/src/facts.rs @@ -590,6 +590,7 @@ pub(crate) struct PluginInstallFailedInput { pub plugin: PluginTelemetryMetadata, pub source: PluginInstallSource, pub error_type: String, + pub sub_error_type: Option, } pub struct ExternalAgentConfigImportCompletedInput { @@ -606,6 +607,7 @@ pub struct ExternalAgentConfigImportFailureInput { pub item_type: String, pub failure_stage: String, pub error_type: String, + pub sub_error_type: Option, } #[derive(Clone, Copy)] diff --git a/codex-rs/analytics/src/reducer.rs b/codex-rs/analytics/src/reducer.rs index d9546e9db0..19425a0f13 100644 --- a/codex-rs/analytics/src/reducer.rs +++ b/codex-rs/analytics/src/reducer.rs @@ -841,6 +841,7 @@ impl AnalyticsReducer { plugin, source, error_type, + sub_error_type, } = input; out.push(TrackEventRequest::PluginInstallFailed( CodexPluginInstallFailedEventRequest { @@ -849,6 +850,7 @@ impl AnalyticsReducer { plugin: codex_plugin_metadata(plugin), source, error_type, + sub_error_type, }, }, )); @@ -888,6 +890,7 @@ impl AnalyticsReducer { item_type: input.item_type, failure_stage: input.failure_stage, error_type: input.error_type, + sub_error_type: input.sub_error_type, product_client_id: Some(originator().value), }, }, diff --git a/codex-rs/app-server-protocol/schema/json/ServerNotification.json b/codex-rs/app-server-protocol/schema/json/ServerNotification.json index 6c73af08af..2b70571738 100644 --- a/codex-rs/app-server-protocol/schema/json/ServerNotification.json +++ b/codex-rs/app-server-protocol/schema/json/ServerNotification.json @@ -1209,6 +1209,12 @@ "string", "null" ] + }, + "subErrorType": { + "type": [ + "string", + "null" + ] } }, "required": [ diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json index fe0deb8bd4..c25c650d81 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json @@ -9366,6 +9366,12 @@ "string", "null" ] + }, + "subErrorType": { + "type": [ + "string", + "null" + ] } }, "required": [ diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json index e637ea4563..59c5e8b9db 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json @@ -5599,6 +5599,12 @@ "string", "null" ] + }, + "subErrorType": { + "type": [ + "string", + "null" + ] } }, "required": [ diff --git a/codex-rs/app-server-protocol/schema/json/v2/ExternalAgentConfigImportCompletedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/ExternalAgentConfigImportCompletedNotification.json index 51d0e95937..aca9194eee 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ExternalAgentConfigImportCompletedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ExternalAgentConfigImportCompletedNotification.json @@ -29,6 +29,12 @@ "string", "null" ] + }, + "subErrorType": { + "type": [ + "string", + "null" + ] } }, "required": [ diff --git a/codex-rs/app-server-protocol/schema/json/v2/ExternalAgentConfigImportHistoriesReadResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ExternalAgentConfigImportHistoriesReadResponse.json index 68921f6b24..3e39fa49ae 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ExternalAgentConfigImportHistoriesReadResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ExternalAgentConfigImportHistoriesReadResponse.json @@ -59,6 +59,12 @@ "string", "null" ] + }, + "subErrorType": { + "type": [ + "string", + "null" + ] } }, "required": [ diff --git a/codex-rs/app-server-protocol/schema/json/v2/ExternalAgentConfigImportProgressNotification.json b/codex-rs/app-server-protocol/schema/json/v2/ExternalAgentConfigImportProgressNotification.json index 32975b2c2d..8bc5d16606 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ExternalAgentConfigImportProgressNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ExternalAgentConfigImportProgressNotification.json @@ -29,6 +29,12 @@ "string", "null" ] + }, + "subErrorType": { + "type": [ + "string", + "null" + ] } }, "required": [ diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ExternalAgentConfigImportItemTypeFailure.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ExternalAgentConfigImportItemTypeFailure.ts index 13e02bf179..f2f6ebc592 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/ExternalAgentConfigImportItemTypeFailure.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ExternalAgentConfigImportItemTypeFailure.ts @@ -3,4 +3,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { ExternalAgentConfigMigrationItemType } from "./ExternalAgentConfigMigrationItemType"; -export type ExternalAgentConfigImportItemTypeFailure = { itemType: ExternalAgentConfigMigrationItemType, errorType: string | null, failureStage: string, message: string, cwd: string | null, source: string | null, }; +export type ExternalAgentConfigImportItemTypeFailure = { itemType: ExternalAgentConfigMigrationItemType, errorType: string | null, subErrorType: string | null, failureStage: string, message: string, cwd: string | null, source: string | null, }; diff --git a/codex-rs/app-server-protocol/src/protocol/v2/config.rs b/codex-rs/app-server-protocol/src/protocol/v2/config.rs index f161b1808b..4770ea8539 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2/config.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2/config.rs @@ -715,6 +715,7 @@ pub struct ExternalAgentConfigImportResponse { pub struct ExternalAgentConfigImportItemTypeFailure { pub item_type: ExternalAgentConfigMigrationItemType, pub error_type: Option, + pub sub_error_type: Option, pub failure_stage: String, pub message: String, pub cwd: Option, diff --git a/codex-rs/app-server/src/external_agent_migration/processor.rs b/codex-rs/app-server/src/external_agent_migration/processor.rs index f064050743..8eee5585cb 100644 --- a/codex-rs/app-server/src/external_agent_migration/processor.rs +++ b/codex-rs/app-server/src/external_agent_migration/processor.rs @@ -640,6 +640,7 @@ fn track_completed_import_notification( item_type: item_type.clone(), failure_stage: failure.failure_stage.clone(), error_type: import_failure_error_type(failure), + sub_error_type: failure.sub_error_type.clone(), }, ); } @@ -692,6 +693,7 @@ async fn record_completed_import_notification( Ok(ExternalAgentConfigImportFailureRecord { item_type: serde_json::from_value(serde_json::to_value(failure.item_type)?)?, error_type: failure.error_type.clone(), + sub_error_type: failure.sub_error_type.clone(), failure_stage: failure.failure_stage.clone(), message: failure.message.clone(), cwd: failure.cwd.clone(), @@ -747,6 +749,7 @@ fn protocol_import_failure_record( Ok(ProtocolImportFailure { item_type: protocol_import_record_item_type(record.item_type)?, error_type: record.error_type, + sub_error_type: record.sub_error_type, failure_stage: record.failure_stage, message: record.message, cwd: record.cwd, @@ -844,6 +847,7 @@ fn protocol_import_raw_error(raw_error: &CoreImportRawError) -> ProtocolImportFa ProtocolImportFailure { item_type: protocol_migration_item_type(raw_error.item_type), error_type: raw_error.error_type.clone(), + sub_error_type: raw_error.sub_error_type.clone(), failure_stage: raw_error.failure_stage.clone(), message: raw_error.message.clone(), cwd: raw_error.cwd.clone(), diff --git a/codex-rs/app-server/src/external_agent_migration/service.rs b/codex-rs/app-server/src/external_agent_migration/service.rs index c6dbd1ec86..bc6cc81967 100644 --- a/codex-rs/app-server/src/external_agent_migration/service.rs +++ b/codex-rs/app-server/src/external_agent_migration/service.rs @@ -170,6 +170,7 @@ pub(crate) struct ExternalAgentConfigImportSuccess { pub(crate) struct ExternalAgentConfigImportRawError { pub item_type: ExternalAgentConfigMigrationItemType, pub error_type: Option, + pub sub_error_type: Option, pub failure_stage: String, pub message: String, pub cwd: Option, @@ -448,6 +449,7 @@ impl ExternalAgentConfigService { item_result.record_error(ExternalAgentConfigImportRawError { item_type, error_type: Some(error_type.to_string()), + sub_error_type: None, failure_stage: "import_request_failed".to_string(), message, cwd: item_result.cwd.clone(), @@ -1068,12 +1070,14 @@ impl ExternalAgentConfigService { Err(err) => { let plugin_id = format!("{plugin_name}@{marketplace_name}"); outcome.failed_plugin_ids.push(plugin_id.clone()); + let sub_error_type = err.sub_error_type(); let mut raw_error = plugin_import_raw_error( cwd, "plugin_import", err.to_string(), Some(plugin_id), ); + raw_error.sub_error_type = sub_error_type; if matches!( err, PluginInstallError::Marketplace( @@ -1660,6 +1664,7 @@ pub(crate) fn record_import_error( result.record_error(ExternalAgentConfigImportRawError { item_type: result.item_type, error_type: None, + sub_error_type: None, failure_stage: failure_stage.to_string(), message: message.into(), cwd: result.cwd.clone(), @@ -1691,6 +1696,7 @@ fn plugin_import_raw_error( ExternalAgentConfigImportRawError { item_type: ExternalAgentConfigMigrationItemType::Plugins, error_type: None, + sub_error_type: None, failure_stage: failure_stage.to_string(), message, cwd: cwd.map(Path::to_path_buf), diff --git a/codex-rs/app-server/src/external_agent_migration/service_tests.rs b/codex-rs/app-server/src/external_agent_migration/service_tests.rs index 88b3063123..2765e7dda2 100644 --- a/codex-rs/app-server/src/external_agent_migration/service_tests.rs +++ b/codex-rs/app-server/src/external_agent_migration/service_tests.rs @@ -49,6 +49,7 @@ fn assert_single_plugin_raw_error( ); assert_eq!(raw_error.failure_stage, failure_stage); assert_eq!(raw_error.error_type.as_deref(), error_type); + assert_eq!(raw_error.sub_error_type, None); assert_eq!(raw_error.cwd, None); assert_eq!(raw_error.source.as_deref(), Some(source)); assert!(!raw_error.message.is_empty()); diff --git a/codex-rs/app-server/src/request_processors/plugins.rs b/codex-rs/app-server/src/request_processors/plugins.rs index 5cf388de6b..bde0ad585f 100644 --- a/codex-rs/app-server/src/request_processors/plugins.rs +++ b/codex-rs/app-server/src/request_processors/plugins.rs @@ -1750,6 +1750,7 @@ impl PluginRequestProcessor { plugin, PluginInstallSource::Manual, error_type.to_string(), + sub_error_type, ); } diff --git a/codex-rs/core-plugins/src/manager.rs b/codex-rs/core-plugins/src/manager.rs index 010455e5c2..b58886f97c 100644 --- a/codex-rs/core-plugins/src/manager.rs +++ b/codex-rs/core-plugins/src/manager.rs @@ -1312,7 +1312,7 @@ impl PluginsManager { self.track_plugin_install_failed( &plugin_id, plugin_install_error_type(&err), - plugin_install_sub_error_type(&err), + err.sub_error_type(), err.to_string(), ); Err(err) @@ -1375,7 +1375,7 @@ impl PluginsManager { self.track_plugin_install_failed( &resolved.plugin_id, plugin_install_error_type(&err), - plugin_install_sub_error_type(&err), + err.sub_error_type(), err.to_string(), ); return Err(err); @@ -1387,7 +1387,7 @@ impl PluginsManager { self.track_plugin_install_failed( &plugin_id, plugin_install_error_type(&err), - plugin_install_sub_error_type(&err), + err.sub_error_type(), err.to_string(), ); Err(err) @@ -1450,6 +1450,7 @@ impl PluginsManager { self.telemetry_metadata_for_plugin_id(plugin_id), self.plugin_install_source, error_type.to_string(), + sub_error_type, ); } } @@ -2728,6 +2729,13 @@ impl PluginInstallError { ) | Self::Store(PluginStoreError::Invalid(_)) ) } + + pub fn sub_error_type(&self) -> Option { + match self { + Self::Store(err) => err.sub_error_type(), + Self::Marketplace(_) | Self::Remote(_) | Self::Config(_) | Self::Join(_) => None, + } + } } fn plugin_install_error_type(err: &PluginInstallError) -> &'static str { @@ -2740,16 +2748,6 @@ 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/manager_tests.rs b/codex-rs/core-plugins/src/manager_tests.rs index 151d2d3768..5a3d6dfd5f 100644 --- a/codex-rs/core-plugins/src/manager_tests.rs +++ b/codex-rs/core-plugins/src/manager_tests.rs @@ -5950,3 +5950,16 @@ fn remote_installed_plugins_cache_refresh_coalesces_materializations() { 0 ); } + +#[test] +fn plugin_install_error_preserves_store_io_sub_error_type() { + let error = PluginInstallError::Store(PluginStoreError::Io { + context: "failed to copy plugin file", + source: std::io::Error::other("copy failed"), + }); + + assert_eq!( + error.sub_error_type(), + Some("failed_to_copy_plugin_file".to_string()) + ); +} diff --git a/codex-rs/state/src/runtime/external_agent_config_imports.rs b/codex-rs/state/src/runtime/external_agent_config_imports.rs index 4fbbe39efc..c6c41feed4 100644 --- a/codex-rs/state/src/runtime/external_agent_config_imports.rs +++ b/codex-rs/state/src/runtime/external_agent_config_imports.rs @@ -18,6 +18,8 @@ pub struct ExternalAgentConfigImportSuccessRecord { pub struct ExternalAgentConfigImportFailureRecord { pub item_type: String, pub error_type: Option, + #[serde(default)] + pub sub_error_type: Option, pub failure_stage: String, pub message: String, pub cwd: Option, diff --git a/codex-rs/state/src/runtime/external_agent_config_imports_tests.rs b/codex-rs/state/src/runtime/external_agent_config_imports_tests.rs index 6d77894d43..5546a0f8f8 100644 --- a/codex-rs/state/src/runtime/external_agent_config_imports_tests.rs +++ b/codex-rs/state/src/runtime/external_agent_config_imports_tests.rs @@ -38,6 +38,7 @@ async fn records_completion_by_import_id() -> anyhow::Result<()> { &[ExternalAgentConfigImportFailureRecord { item_type: "MCP_SERVER_CONFIG".to_string(), error_type: None, + sub_error_type: Some("failed_to_copy_plugin_file".to_string()), failure_stage: "import".to_string(), message: "failed".to_string(), cwd: None, @@ -68,6 +69,7 @@ async fn records_completion_by_import_id() -> anyhow::Result<()> { failures: vec![ExternalAgentConfigImportFailureRecord { item_type: "MCP_SERVER_CONFIG".to_string(), error_type: None, + sub_error_type: Some("failed_to_copy_plugin_file".to_string()), failure_stage: "import".to_string(), message: "failed".to_string(), cwd: None, @@ -106,6 +108,7 @@ async fn records_completion_by_import_id() -> anyhow::Result<()> { vec![ExternalAgentConfigImportFailureRecord { item_type: "MCP_SERVER_CONFIG".to_string(), error_type: None, + sub_error_type: Some("failed_to_copy_plugin_file".to_string()), failure_stage: "import".to_string(), message: "failed".to_string(), cwd: None, diff --git a/codex-rs/tui/src/external_agent_config_migration_flow_tests.rs b/codex-rs/tui/src/external_agent_config_migration_flow_tests.rs index 9a241eee7b..14aea2f475 100644 --- a/codex-rs/tui/src/external_agent_config_migration_flow_tests.rs +++ b/codex-rs/tui/src/external_agent_config_migration_flow_tests.rs @@ -123,6 +123,7 @@ fn completed_notification() -> ExternalAgentConfigImportCompletedNotification { failures: vec![ExternalAgentConfigImportItemTypeFailure { item_type: ExternalAgentConfigMigrationItemType::Plugins, error_type: Some("plugin_install_failed".to_string()), + sub_error_type: Some("failed_to_copy_plugin_file".to_string()), failure_stage: "plugin_import".to_string(), message: "install failed".to_string(), cwd: Some(PathBuf::from("/workspace/project")),