{"record":{"id":"19b02f605ffa222e","repo":"argoproj/argo-workflows","slug":"plugin-s-delete-failed-s","errorCode":null,"errorMessage":"plugin %s delete failed: %s","messagePattern":"plugin (.+?) delete failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/artifacts/plugin/plugin.go","lineNumber":336,"sourceCode":"// saveStreamViaTempFile is the fallback used when the plugin doesn't implement\n// streaming SaveStream: buffer to a temp file and call the existing unary Save.\nfunc (d *Driver) saveStreamViaTempFile(ctx context.Context, reader io.Reader, outputArtifact *wfv1.Artifact) error {\n\treturn common.SaveStreamViaTempFile(reader, \"plugin-upload-*\", func(path string) error {\n\t\treturn d.Save(ctx, path, outputArtifact)\n\t})\n}\n\n// Delete implements ArtifactDriver.Delete by calling the plugin service\nfunc (d *Driver) Delete(ctx context.Context, artifactRef *wfv1.Artifact) error {\n\tgrpcArtifact := convertToGRPC(artifactRef)\n\tresp, err := d.client.Delete(ctx, &artifact.DeleteArtifactRequest{\n\t\tArtifact: grpcArtifact,\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"plugin %s delete failed: %w\", d.pluginName, err)\n\t}\n\tif !resp.Success {\n\t\treturn fmt.Errorf(\"plugin %s delete failed: %s\", d.pluginName, resp.Error)\n\t}\n\treturn nil\n}\n\n// ListObjects implements ArtifactDriver.ListObjects by calling the plugin service\nfunc (d *Driver) ListObjects(ctx context.Context, artifactRef *wfv1.Artifact) ([]string, error) {\n\tgrpcArtifact := convertToGRPC(artifactRef)\n\tresp, err := d.client.ListObjects(ctx, &artifact.ListObjectsRequest{\n\t\tArtifact: grpcArtifact,\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"plugin %s list objects failed: %w\", d.pluginName, err)\n\t}\n\tif resp.Error != \"\" {\n\t\treturn nil, fmt.Errorf(\"plugin %s list objects failed: %s\", d.pluginName, resp.Error)\n\t}\n\treturn resp.Objects, nil\n}","sourceCodeStart":318,"sourceCodeEnd":354,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/artifacts/plugin/plugin.go#L318-L354","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"// caller treating already-deleted as success\n// before:\nerr := driver.Delete(ctx, art)\n// after:\nif err := driver.Delete(ctx, art); err != nil {\n\tif strings.Contains(err.Error(), \"NoSuchKey\") || strings.Contains(err.Error(), \"not found\") {\n\t\treturn nil // idempotent delete\n\t}\n\treturn err\n}","handlingStrategy":"try-catch","validationCode":"// only delete artifacts known to exist (track provenance)\nif art == nil || art.GetName() == \"\" || art.GetArtifactGC().GetStrategy() == wfv1.ArtifactGCNever {\n\treturn nil // nothing to delete / deletion disabled\n}","typeGuard":"func isPluginLogicalDeleteFailure(err error) bool {\n\t// error 512 has no gRPC status (RPC succeeded); detect by absence of a status code\n\treturn err != nil && status.Code(err) == codes.Unknown && strings.Contains(err.Error(), \"delete failed\")\n}","tryCatchPattern":"if err := driver.Delete(ctx, art); err != nil {\n\tvar msg string\n\tif isPluginLogicalDeleteFailure(err) {\n\t\tmsg = strings.TrimPrefix(err.Error(), \"plugin \"+pluginName+\" delete failed: \")\n\t}\n\tswitch {\n\tcase strings.Contains(msg, \"not found\"), strings.Contains(msg, \"NoSuchKey\"):\n\t\treturn nil // already gone: idempotent success\n\tdefault:\n\t\treturn err\n\t}\n}","preventionTips":["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."],"tags":["plugin","artifacts","delete","storage-backend"],"backgroundTag":"plugin-reported-failure","analyzedSha":"35bff19146f5a6ada77468c431f2624bd577e373","analyzedAt":"2026-09-03T19:34:35.908Z","contentChangedAt":"2026-09-03T19:34:35.908Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}