argoproj/argo-workflows · error

unable to upload stream to Azure blob %s: %w

Error message

unable to upload stream to Azure blob %s: %w

What it means

SaveStream calls blobClient.UploadStream to write the reader's contents to Azure. If the streaming upload fails (auth, throttling, connection reset mid-stream, invalid blob name), the SDK error is wrapped with this message.

Source

Thrown at workflow/artifacts/azure/azure.go:322

	return nil
}

// SaveStream saves an artifact from an io.Reader to Azure Blob Storage
func (azblobDriver *ArtifactDriver) SaveStream(ctx context.Context, reader io.Reader, outputArtifact *wfv1.Artifact) error {
	logger := logging.RequireLoggerFromContext(ctx)
	logger.WithField("endpoint", outputArtifact.Azure.Endpoint).
		WithField("container", outputArtifact.Azure.Container).
		WithField("blob", outputArtifact.Azure.Blob).
		Info(ctx, "Streaming to Azure Blob Storage")

	containerClient, err := azblobDriver.newAzureContainerClient(ctx)
	if err != nil {
		return fmt.Errorf("unable to create Azure Blob Container client for %s: %w", outputArtifact.Azure.Blob, err)
	}

	blobClient := containerClient.NewBlockBlobClient(outputArtifact.Azure.Blob)
	if _, err = blobClient.UploadStream(ctx, reader, nil); err != nil {
		return fmt.Errorf("unable to upload stream to Azure blob %s: %w", outputArtifact.Azure.Blob, err)
	}
	return nil
}

// PutFile uploads a file to Azure Blob Storage
func PutFile(ctx context.Context, containerClient *container.Client, blobName, path string) error {
	blobClient := containerClient.NewBlockBlobClient(blobName)

	file, err := os.Open(path)
	if err != nil {
		return fmt.Errorf("unable to open file %s: %w", path, err)
	}
	defer func() {
		if closeErr := file.Close(); closeErr != nil {
			logger := logging.RequireLoggerFromContext(ctx)
			logger.WithError(closeErr).Warn(ctx, "unable to close file")
		}
	}()

View on GitHub (pinned to 35bff19146)

Solutions

  1. Inspect the wrapped Azure error code (AuthorizationFailure, ServerBusy, etc.) and address it.
  2. Retry the operation; UploadStream errors are often transient network failures.
  3. Confirm write permissions (Storage Blob Data Contributor) for the key/identity.
  4. Check storage account firewall/network rules.
  5. For very large streams, increase timeouts and ensure stable connectivity.

Example fix

// before: no retry around streaming upload
err := driver.SaveStream(ctx, reader, artifact)
// after: retry transient failures
wait.Backoff(... ) // retry SaveStream on 5xx/timeout errors
Defensive patterns

Strategy: retry

Validate before calling

if reader == nil {
  return errors.New("nil reader passed to SaveStream")
}
if artifact.Azure.Blob == "" {
  return errors.New("blob name is empty")
}

Type guard

func isRetryableUploadErr(err error) bool {
  var re *azcore.ResponseError
  if !errors.As(err, &re) { return false }
  return re.StatusCode == 429 || re.StatusCode >= 500 || errors.Is(err, io.ErrUnexpectedEOF)
}

Try / catch

err := retry.OnError(wait.Backoff{Steps: 4, Duration: time.Second, Factor: 2},
  isRetryableUploadErr, func() error {
    return driver.SaveStream(ctx, reader, artifact)
  })

Prevention

When it happens

Trigger: Uploading an io.Reader (e.g. container logs, large artifacts) when the network drops mid-stream, the account is throttled, credentials lack write permission, or the blob name is invalid.

Common situations: Streaming very large logs over flaky connections; 429 throttling from heavy artifact traffic; container-level firewall blocking; RBAC/key lacking write on the container.

Related errors


AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03). Data as JSON: /api/errors/5efe67b92e9c5733. Report an issue: GitHub.