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
This commit is contained in:
charlesgong-openai
2026-07-14 22:21:27 +00:00
committed by copyberry
parent 44954d1b4b
commit 3fae30dd85
22 changed files with 114 additions and 29 deletions
+11 -13
View File
@@ -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<String> {
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<String> {
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",
@@ -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())
);
}