argoproj/argo-workflows · error
plugin %s delete failed: %s
Error message
plugin %s delete failed: %s
What it means
After a successful Delete RPC, the plugin reports its own logical outcome via resp.Success and an error string. When Success is false, the driver raises "plugin %s delete failed: %s" with the plugin-provided message. The gRPC call worked; the plugin (or its backing storage) refused or failed the delete.
Source
Thrown at workflow/artifacts/plugin/plugin.go:336
// saveStreamViaTempFile is the fallback used when the plugin doesn't implement
// streaming SaveStream: buffer to a temp file and call the existing unary Save.
func (d *Driver) saveStreamViaTempFile(ctx context.Context, reader io.Reader, outputArtifact *wfv1.Artifact) error {
return common.SaveStreamViaTempFile(reader, "plugin-upload-*", func(path string) error {
return d.Save(ctx, path, outputArtifact)
})
}
// Delete implements ArtifactDriver.Delete by calling the plugin service
func (d *Driver) Delete(ctx context.Context, artifactRef *wfv1.Artifact) error {
grpcArtifact := convertToGRPC(artifactRef)
resp, err := d.client.Delete(ctx, &artifact.DeleteArtifactRequest{
Artifact: grpcArtifact,
})
if err != nil {
return fmt.Errorf("plugin %s delete failed: %w", d.pluginName, err)
}
if !resp.Success {
return fmt.Errorf("plugin %s delete failed: %s", d.pluginName, resp.Error)
}
return nil
}
// ListObjects implements ArtifactDriver.ListObjects by calling the plugin service
func (d *Driver) ListObjects(ctx context.Context, artifactRef *wfv1.Artifact) ([]string, error) {
grpcArtifact := convertToGRPC(artifactRef)
resp, err := d.client.ListObjects(ctx, &artifact.ListObjectsRequest{
Artifact: grpcArtifact,
})
if err != nil {
return nil, fmt.Errorf("plugin %s list objects failed: %w", d.pluginName, err)
}
if resp.Error != "" {
return nil, fmt.Errorf("plugin %s list objects failed: %s", d.pluginName, resp.Error)
}
return resp.Objects, nil
}View on GitHub (pinned to 35bff19146)
Solutions
- Read the %s message from the plugin — it names the storage-level cause (e.g. NoSuchKey, AccessDenied) and fix accordingly.
- If the object is already gone, treat the delete as successful; many plugins report failure for missing keys — check idempotency of your cleanup logic.
- Verify the plugin's storage credentials and bucket/container configuration are current.
- Ensure the artifact was created by the same plugin/backend currently configured; reconfigure or delete via the original backend.
Example fix
// caller treating already-deleted as success
// before:
err := driver.Delete(ctx, art)
// after:
if err := driver.Delete(ctx, art); err != nil {
if strings.Contains(err.Error(), "NoSuchKey") || strings.Contains(err.Error(), "not found") {
return nil // idempotent delete
}
return err
} Defensive patterns
Strategy: try-catch
Validate before calling
// only delete artifacts known to exist (track provenance)
if art == nil || art.GetName() == "" || art.GetArtifactGC().GetStrategy() == wfv1.ArtifactGCNever {
return nil // nothing to delete / deletion disabled
} Type guard
func isPluginLogicalDeleteFailure(err error) bool {
// error 512 has no gRPC status (RPC succeeded); detect by absence of a status code
return err != nil && status.Code(err) == codes.Unknown && strings.Contains(err.Error(), "delete failed")
} Try / catch
if err := driver.Delete(ctx, art); err != nil {
var msg string
if isPluginLogicalDeleteFailure(err) {
msg = strings.TrimPrefix(err.Error(), "plugin "+pluginName+" delete failed: ")
}
switch {
case strings.Contains(msg, "not found"), strings.Contains(msg, "NoSuchKey"):
return nil // already gone: idempotent success
default:
return err
}
} Prevention
- Verify plugin storage credentials and bucket config before enabling artifact GC.
- Don't switch artifact plugins mid-workflow-lifecycle; deleted artifacts must be managed by the same backend that created them.
- Write GC tests covering already-deleted and never-existed keys.
- Capture the plugin's Error message in audit logs for diagnosis.
When it happens
Trigger: Calling Driver.Delete where the plugin responds Success=false, e.g. the artifact key does not exist in the plugin's storage backend, the plugin lacks permissions on the object, or the backend returned a storage-level error that the plugin mapped into the response's Error field.
Common situations: Retrying deletion of an already-deleted artifact; misconfigured plugin storage credentials/region so the backend denies the operation; artifact key from a different plugin/backend than the one now configured (switched plugin config mid-lifecycle).
Related errors
- plugin %s delete failed: %w
- plugin %s list objects failed: %s
- failed to create plugin driver for %s: %w
- plugin %s stream error: %s
- plugin %s save failed: %w
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/19b02f605ffa222e.
Report an issue: GitHub.