{"record":{"id":"79eca308c5b75544","repo":"argoproj/argo-workflows","slug":"plugin-s-save-stream-failed-to-read-artifact-cont","errorCode":null,"errorMessage":"plugin %s save stream failed to read artifact content: %w","messagePattern":"plugin (.+?) save stream failed to read artifact content: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/artifacts/plugin/plugin.go","lineNumber":288,"sourceCode":"\n\tbuf := make([]byte, saveStreamChunkSize)\n\tfor {\n\t\tn, readErr := reader.Read(buf)\n\t\tif n > 0 {\n\t\t\t// gRPC forbids mutating a message after Send (a lazy stats handler may\n\t\t\t// read it later), so copy the chunk instead of aliasing buf, which the\n\t\t\t// next Read overwrites.\n\t\t\tchunk := make([]byte, n)\n\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.","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/artifacts/plugin/plugin.go#L270-L306","documentation":"SaveStream failed while reading bytes from the caller-provided io.Reader (the artifact content source, e.g. a container file or another driver's stream). This is not a plugin or gRPC problem — the error after the prefix is the underlying reader's failure and the stream is abandoned without a final CloseAndRecv success path.","triggerScenarios":"Calling Driver.SaveStream with a reader whose Read returns a non-EOF error mid-stream — e.g. the file being saved disappeared, a disk read error, or an upstream source stream (like an OpenStream pipe from error 500) failed.","commonSituations":"Saving an artifact from a container path deleted during execution, saving from a plugin Load pipe that itself errored (compounded stream errors), disk I/O errors on the executor, reader closed by another goroutine.","solutions":["Look at the wrapped error after 'failed to read artifact content: ' for the real cause (file not found, closed pipe, I/O error)","Verify the source file/path still exists and is readable on the executor at save time","If saving from another stream, fix the upstream stream error first — this error is usually secondary","Add retry logic around content generation if the source is transient (e.g. network-mounted storage)"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Verify the source content is readable before streaming:\nf, err := os.Open(sourcePath)\nif err != nil {\n    return fmt.Errorf(\"artifact source unreadable before SaveStream: %w\", err)\n}\ndefer f.Close()","typeGuard":"func isContentReadError(err error) bool {\n    return strings.Contains(err.Error(), \"failed to read artifact content\")\n}","tryCatchPattern":"if err := driver.SaveStream(ctx, reader, artifact); err != nil {\n    if isContentReadError(err) {\n        // local reader failed — no plugin involvement; fix or regenerate the source, then retry\n        return fmt.Errorf(\"local content source failed: %w\", err)\n    }\n    return err\n}","preventionTips":["Ensure artifact source files are not deleted before the save stage completes","When saving from another stream (e.g. a plugin Load pipe), fix upstream errors first","Use retryable/regeneratable sources for artifact content","Check executor disk health if read errors recur"],"tags":["go","io","plugin","artifacts","streaming"],"backgroundTag":"read-error","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"}