argoproj/argo-workflows · error
unable to upload directory %s to Azure: %w
Error message
unable to upload directory %s to Azure: %w
What it means
Save uploads a whole directory via PutDirectory, which walks the tree and calls PutFile per entry. Any per-file upload failure (auth, throttling, network, blob-name issues) surfaces as this wrapped error.
Source
Thrown at workflow/artifacts/azure/azure.go:295
logger.WithField("endpoint", outputArtifact.Azure.Endpoint).
WithField("container", outputArtifact.Azure.Container).
WithField("blob", outputArtifact.Azure.Blob).
Info(ctx, "Saving to Azure Blob Storage")
isDir, err := file.IsDirectory(path)
if err != nil {
return fmt.Errorf("failed to test if %s is a directory: %w", path, err)
}
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)
}
if isDir {
err := PutDirectory(ctx, containerClient, outputArtifact.Azure.Blob, path)
if err != nil {
return fmt.Errorf("unable to upload directory %s to Azure: %w", path, err)
}
} else {
err := PutFile(ctx, containerClient, outputArtifact.Azure.Blob, path)
if err != nil {
return fmt.Errorf("unable to upload file %s to Azure: %w", path, err)
}
}
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")View on GitHub (pinned to 35bff19146)
Solutions
- Look at the inner PutFile error for the specific file and Azure error code.
- Retry the workflow if the failure was transient (429/503/timeout).
- Reduce artifact size/count or increase timeouts; avoid saving huge trees as artifacts.
- Ensure account key permissions allow blob writes (Storage Blob Data Contributor).
- Sanitize unusual characters in file names before they become blob keys.
Example fix
// before: saving a node_modules-style huge dir as artifact
artifacts: [{name: workdir, path: /work}]
// after: exclude volatile dirs or archive first
artifacts: [{name: workdir, path: /work/archive.tgz}] Defensive patterns
Strategy: retry
Validate before calling
count := 0
filepath.WalkDir(path, func(p string, d fs.DirEntry, err error) error {
count++
return nil
})
if count > 10000 {
return fmt.Errorf("refusing to save %d files as one artifact", count)
} Type guard
func isThrottled(err error) bool {
var re *azcore.ResponseError
return errors.As(err, &re) && re.StatusCode == 429
} Try / catch
err := driver.Save(ctx, dir, artifact)
if err != nil && strings.Contains(err.Error(), "unable to upload directory") {
if isThrottled(errors.Unwrap(err)) {
// backoff and retry
}
return err
} Prevention
- Archive large directory trees into a single file before saving.
- Exclude dependency/volatile directories from artifact paths.
- Prefer retry with backoff for large multi-file uploads.
- Keep blob-name-safe file names (avoid reserved/odd characters).
When it happens
Trigger: Saving a directory artifact when one of the recursive PutFile uploads fails: Azure authorization errors, upload stream failures, invalid blob names derived from odd file paths, request timeouts on large trees.
Common situations: Uploading directories with thousands of files and hitting throttling; file names with characters Azure rejects; intermittent network drops in-cluster; key rotation mid-upload.
Related errors
- unable to upload file %s to Azure: %w
- unable to open stream for blob %s: %w
- unable to test if blob %s is a directory: %w
- unable to open blob stream for %s: %w
- unable to upload stream to Azure blob %s: %w
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/2591101eb674b7a5.
Report an issue: GitHub.