{"record":{"id":"43a6d516a75bdb3a","repo":"argoproj/argo-workflows","slug":"plugin-s-stream-write-failed-w","errorCode":null,"errorMessage":"plugin %s stream write failed: %w","messagePattern":"plugin (.+?) stream write failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"workflow/artifacts/plugin/plugin.go","lineNumber":194,"sourceCode":"\t\tfor {\n\t\t\tresp, err := stream.Recv()\n\t\t\tif errors.Is(err, io.EOF) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\twriter.CloseWithError(fmt.Errorf(\"plugin %s stream receive failed: %w\", d.pluginName, err))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif resp.Error != \"\" {\n\t\t\t\twriter.CloseWithError(fmt.Errorf(\"plugin %s stream error: %s\", d.pluginName, resp.Error))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif resp.IsEnd {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif len(resp.Data) > 0 {\n\t\t\t\tif _, writeErr := writer.Write(resp.Data); writeErr != nil {\n\t\t\t\t\twriter.CloseWithError(fmt.Errorf(\"plugin %s stream write failed: %w\", d.pluginName, writeErr))\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n\n\treturn reader, nil\n}\n\n// Save implements ArtifactDriver.Save by calling the plugin service\nfunc (d *Driver) Save(ctx context.Context, path string, outputArtifact *wfv1.Artifact) error {\n\tgrpcArtifact := convertToGRPC(outputArtifact)\n\tresp, err := d.client.Save(ctx, &artifact.SaveArtifactRequest{\n\t\tPath:           path,\n\t\tOutputArtifact: grpcArtifact,\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"plugin %s save failed: %w\", d.pluginName, err)","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/artifacts/plugin/plugin.go#L176-L212","documentation":"This error occurs in the OpenStream pump goroutine when writing a received plugin chunk into the io.Pipe fails via writer.Write. Since the pipe's read side is handed to the caller, a write error almost always means the consumer closed or stopped reading the pipe (or the pipe was already closed with an error). The driver wraps the write error with the plugin name and tears down the stream.","triggerScenarios":"Calling Driver.OpenStream and then closing or abandoning the returned io.ReadCloser before the plugin finishes streaming, so the driver goroutine's writer.Write fails with io.ErrClosedPipe.","commonSituations":"Caller uses io.Copy with a context timeout and gives up early; artifact loading code errors elsewhere and closes the reader; double-close of the ReadCloser; process shutting down mid-stream.","solutions":["Check whether your code closes the returned ReadCloser before fully reading it — early Close triggers this","Ensure you do not close the reader while the plugin stream is still being consumed","If abandoning a stream, cancel the context passed to OpenStream so the goroutine exits cleanly","Inspect the wrapped writeErr after 'stream write failed: ' for the actual pipe error (usually io.ErrClosedPipe)"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isClosedPipe(err error) bool {\n    return errors.Is(err, io.ErrClosedPipe) || errors.Is(err, os.ErrClosed)\n}","tryCatchPattern":"reader, err := driver.OpenStream(ctx, artifact)\nif err != nil { return err }\n_, copyErr := io.Copy(dst, reader)\nreader.Close()\nif copyErr != nil {\n    if isClosedPipe(copyErr) {\n        // consumer closed early; ensure ctx cancel to stop the driver goroutine\n        cancel()\n        return nil // or handle as intentional early termination\n    }\n    return copyErr\n}","preventionTips":["Never close the OpenStream ReadCloser before reading to completion unless intentionally aborting","Cancel the context when abandoning a stream so the pump goroutine exits cleanly","Avoid double-closing the returned reader","Check for early returns in your copy loop (deadlines, size limits) that close the pipe mid-stream"],"tags":["go","io","pipe","plugin","streaming"],"backgroundTag":"broken-pipe","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"}