rustfs/rustfs · error · TargetPluginExternalActionError

plugin {plugin_id} is not an external plugin

Error message

plugin {plugin_id} is not an external plugin

What it means

TargetPluginExternalActionError::NotExternalPlugin is returned by validate_external_action_subject (crates/targets/src/control_plane.rs:424-432), the first check plan_external_target_plugin_action performs. The external planning flow only accepts marketplace manifests whose packaging field equals TargetPluginPackaging::External; builtin-packaged plugins (e.g. builtin:webhook) are managed through the builtin registration path instead.

Source

Thrown at crates/targets/src/control_plane.rs:261

pub struct TargetPluginExternalFlowGateStatus {
    pub enabled: bool,
    pub install_requires_signature: bool,
    pub install_requires_provenance: bool,
    pub runtime_allows_external_sidecars: bool,
    pub runtime_requires_sandbox: bool,
    pub runtime_requires_provenance: bool,
    pub circuit_breaker_closed: bool,
    pub max_queue_depth: usize,
    pub failure_threshold: usize,
    pub redacts_error_details: bool,
}

#[derive(Debug, Error, PartialEq, Eq)]
pub enum TargetPluginExternalActionError {
    #[error("external plugin flow is disabled")]
    ExternalFlowDisabled,

    #[error("plugin {plugin_id} is not an external plugin")]
    NotExternalPlugin { plugin_id: String },

    #[error("plugin {plugin_id} is not installed")]
    NotInstalled { plugin_id: String },

    #[error("plugin {plugin_id} has no previous revision for rollback")]
    MissingPreviousRevision { plugin_id: String },

    #[error("external plugin install policy denied action: {reason}")]
    InstallPolicyDenied { reason: String },

    #[error("external plugin runtime policy denied action: {reason}")]
    RuntimePolicyDenied { reason: String },

    #[error("external plugin circuit breaker is open")]
    CircuitBreakerOpen,

    #[error("external plugin {plugin_id} has no installable artifact for host target triple {target_triple}")]

View on GitHub (pinned to 35af688cd9)

Solutions

  1. Branch on manifest.packaging: use the builtin registry/installation path (builtin_target_plugin_installation) for builtin plugins and the external flow only for TargetPluginPackaging::External
  2. Use an external-packaged manifest such as example_external_webhook_plugin().manifest when exercising the external flow
  3. Set packaging = TargetPluginPackaging::External in the marketplace manifest if the plugin really ships as a downloadable sidecar artifact

Example fix

// before
let result = plan_external_target_plugin_action(
    &builtin_target_marketplace_manifest("webhook"), // packaging: Builtin
    TargetPluginExternalAction::Install, &inst, &gate, triple); // NotExternalPlugin

// after
if manifest.packaging == TargetPluginPackaging::External {
    plan_external_target_plugin_action(&manifest, action, &inst, &gate, triple)?;
} else {
    let installation = builtin_target_plugin_installation(&manifest); // builtin path
}
Defensive patterns

Strategy: validation

Validate before calling

use crate::manifest::TargetPluginPackaging;

if manifest.packaging != TargetPluginPackaging::External {
    // route to the builtin registration/installation path instead
    let installation = builtin_target_plugin_installation(&builtin_manifest);
}

Type guard

fn is_external_plugin(manifest: &TargetPluginMarketplaceManifest) -> bool {
    manifest.packaging == TargetPluginPackaging::External
}

Try / catch

match plan_external_target_plugin_action(&manifest, action, &inst, &gate, triple) {
    Err(TargetPluginExternalActionError::NotExternalPlugin { plugin_id }) => /* use the builtin path for plugin_id */,
    Ok(decision) => { /* apply */ }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Passing builtin_target_marketplace_manifest("webhook") or any manifest with packaging != External to plan_external_target_plugin_action, for any action (Install, Enable, Disable, Rollback).

Common situations: Admin handlers routing every plugin through the external flow; tests using builtin manifests as convenient fixtures; a manifest author forgetting packaging: external in marketplace metadata.

Related errors


AI-assisted analysis of rustfs/rustfs@35af688cd9 (2026-08-20). Data as JSON: /api/errors/51e5d818f339df76. Report an issue: GitHub.