{"record":{"id":"dd640ff5b1a1cc93","repo":"argoproj/argo-workflows","slug":"plugin-s-save-stream-failed-s","errorCode":null,"errorMessage":"plugin %s save stream failed: %s","messagePattern":"plugin (.+?) save stream failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/artifacts/plugin/plugin.go","lineNumber":297,"sourceCode":"\t\t\tcopy(chunk, buf[:n])\n\t\t\tif sendErr := sendFrame(&artifact.SaveStreamArtifactRequest{Chunk: chunk}, \"mid-transfer\"); sendErr != nil {\n\t\t\t\treturn sendErr\n\t\t\t}\n\t\t}\n\t\tif errors.Is(readErr, io.EOF) {\n\t\t\tbreak\n\t\t}\n\t\tif readErr != nil {\n\t\t\treturn fmt.Errorf(\"plugin %s save stream failed to read artifact content: %w\", d.pluginName, readErr)\n\t\t}\n\t}\n\n\tresp, err := stream.CloseAndRecv()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"plugin %s save stream failed: %w\", d.pluginName, err)\n\t}\n\tif !resp.Success {\n\t\treturn fmt.Errorf(\"plugin %s save stream failed: %s\", d.pluginName, resp.Error)\n\t}\n\treturn nil\n}\n\n// supportsSaveStream reports whether the plugin advertises streaming support.\n// A plugin that predates GetCapabilities returns codes.Unimplemented, which maps to\n// (false, nil) so SaveStream falls back to the buffered Save. Any other error is\n// returned rather than silently downgrading to buffering a potentially large artifact\n// to disk before the real failure would resurface via Save.\nfunc (d *Driver) supportsSaveStream(ctx context.Context) (bool, error) {\n\tresp, err := d.client.GetCapabilities(ctx, &artifact.GetCapabilitiesRequest{})\n\tif err != nil {\n\t\tif status.Code(err) == codes.Unimplemented {\n\t\t\treturn false, nil\n\t\t}\n\t\treturn false, fmt.Errorf(\"plugin %s get capabilities failed: %w\", d.pluginName, err)\n\t}\n\treturn resp.GetSupportsSaveStream(), nil","sourceCodeStart":279,"sourceCodeEnd":315,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/artifacts/plugin/plugin.go#L279-L315","documentation":"The SaveStream RPC completed at the transport level and returned a SaveResponse, but the plugin set Success=false with an application-level Error string. The driver surfaces the plugin's own message after 'save stream failed: '. This is plugin business-logic failure after receiving the full stream — not a network or protocol issue.","triggerScenarios":"Calling Driver.SaveStream where stream.CloseAndRecv returns a response with resp.Success == false; the plugin received all chunks but rejected or failed the save, reporting why in resp.Error.","commonSituations":"Plugin rejected the artifact metadata (invalid key/path/flags), backend rejected the finalized object (permissions, quota, encryption policy), plugin-side validation of total size or checksum failed.","solutions":["Read the message after 'plugin <name> save stream failed: ' — it is the plugin's specific rejection reason","Fix the artifact spec fields the plugin complained about (key, path, flags, metadata)","Check destination storage permissions/quotas from the plugin pod if the error points at the backend","Check plugin logs for the fuller error context behind the reported string"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Validate artifact metadata the plugin will receive before streaming:\nif outputArtifact.ArtifactLocation == nil || outputArtifact.ArtifactLocation.Key == nil {\n    return fmt.Errorf(\"output artifact missing location/key for plugin save\")\n}","typeGuard":"func pluginRejectedSave(err error) (string, bool) {\n    s := err.Error()\n    if strings.Contains(s, \"save stream failed: \") {\n        i := strings.LastIndex(s, \"save stream failed: \")\n        return s[i+len(\"save stream failed: \"):], true\n    }\n    return \"\", false\n}","tryCatchPattern":"if err := driver.SaveStream(ctx, reader, artifact); err != nil {\n    if reason, ok := pluginRejectedSave(err); ok {\n        // deterministic plugin rejection: fix artifact config/permissions, do not blind-retry\n        return fmt.Errorf(\"plugin rejected artifact (%s): %w\", reason, err)\n    }\n    return err\n}","preventionTips":["Read the plugin's rejection reason — it distinguishes fixable config issues from backend outages","Validate artifact keys/paths against the plugin's naming rules before submission","Confirm destination backend permissions and quotas from the plugin pod","Log the full plugin response in workflows to speed diagnosis"],"tags":["plugin","artifacts","streaming","application-error"],"backgroundTag":"plugin-save-failed","analyzedSha":"35bff19146f5a6ada77468c431f2624bd577e373","analyzedAt":"2026-09-03T19:34:35.908Z","contentChangedAt":"2026-09-03T19:34:35.908Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}