argoproj/argo-workflows · error
plugin %s save stream failed: %s
Error message
plugin %s save stream failed: %s
What it means
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.
Source
Thrown at workflow/artifacts/plugin/plugin.go:297
copy(chunk, buf[:n])
if sendErr := sendFrame(&artifact.SaveStreamArtifactRequest{Chunk: chunk}, "mid-transfer"); sendErr != nil {
return sendErr
}
}
if errors.Is(readErr, io.EOF) {
break
}
if readErr != nil {
return fmt.Errorf("plugin %s save stream failed to read artifact content: %w", d.pluginName, readErr)
}
}
resp, err := stream.CloseAndRecv()
if err != nil {
return fmt.Errorf("plugin %s save stream failed: %w", d.pluginName, err)
}
if !resp.Success {
return fmt.Errorf("plugin %s save stream failed: %s", d.pluginName, resp.Error)
}
return nil
}
// supportsSaveStream reports whether the plugin advertises streaming support.
// A plugin that predates GetCapabilities returns codes.Unimplemented, which maps to
// (false, nil) so SaveStream falls back to the buffered Save. Any other error is
// returned rather than silently downgrading to buffering a potentially large artifact
// to disk before the real failure would resurface via Save.
func (d *Driver) supportsSaveStream(ctx context.Context) (bool, error) {
resp, err := d.client.GetCapabilities(ctx, &artifact.GetCapabilitiesRequest{})
if err != nil {
if status.Code(err) == codes.Unimplemented {
return false, nil
}
return false, fmt.Errorf("plugin %s get capabilities failed: %w", d.pluginName, err)
}
return resp.GetSupportsSaveStream(), nilView on GitHub (pinned to 35bff19146)
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
Defensive patterns
Strategy: validation
Validate before calling
// Validate artifact metadata the plugin will receive before streaming:
if outputArtifact.ArtifactLocation == nil || outputArtifact.ArtifactLocation.Key == nil {
return fmt.Errorf("output artifact missing location/key for plugin save")
} Type guard
func pluginRejectedSave(err error) (string, bool) {
s := err.Error()
if strings.Contains(s, "save stream failed: ") {
i := strings.LastIndex(s, "save stream failed: ")
return s[i+len("save stream failed: "):], true
}
return "", false
} Try / catch
if err := driver.SaveStream(ctx, reader, artifact); err != nil {
if reason, ok := pluginRejectedSave(err); ok {
// deterministic plugin rejection: fix artifact config/permissions, do not blind-retry
return fmt.Errorf("plugin rejected artifact (%s): %w", reason, err)
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- plugin %s stream error: %s
- plugin %s save failed: %s
- plugin %s save stream failed to open: %w
- plugin %s save stream failed %s: %w
- plugin %s save stream failed to read artifact content: %w
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/dd640ff5b1a1cc93.
Report an issue: GitHub.