argoproj/argo-workflows · error
failed to put directory: %w
Error message
failed to put directory: %w
What it means
The upload itself failed: after bucket handling, PutDirectory (if the local path is a directory) could not upload the directory contents to the S3 key prefix. The whole artifact save fails and the retry wrapper decides whether to retry.
Source
Thrown at workflow/artifacts/s3/s3.go:354
if createBucketIfNotPresent != nil {
log.WithField("bucket", outputArtifact.S3.Bucket).Info(ctx, "creating bucket")
makeBucketErr := s3cli.MakeBucket(outputArtifact.S3.Bucket, minio.MakeBucketOptions{
Region: outputArtifact.S3.Region,
ObjectLocking: outputArtifact.S3.CreateBucketIfNotPresent.ObjectLocking,
})
alreadyExists := bucketAlreadyExistsErr(makeBucketErr)
log.WithField("bucket", outputArtifact.S3.Bucket).
WithField("alreadyExists", alreadyExists).
WithError(makeBucketErr).
Info(ctx, "create bucket failed")
if makeBucketErr != nil && !alreadyExists {
return !isTransientS3Err(ctx, makeBucketErr), fmt.Errorf("failed to create bucket %s: %w", outputArtifact.S3.Bucket, makeBucketErr)
}
}
if isDir {
if err = s3cli.PutDirectory(outputArtifact.S3.Bucket, outputArtifact.S3.Key, path); err != nil {
return !isTransientS3Err(ctx, err), fmt.Errorf("failed to put directory: %w", err)
}
} else {
if err = s3cli.PutFile(outputArtifact.S3.Bucket, outputArtifact.S3.Key, path); err != nil {
return !isTransientS3Err(ctx, err), fmt.Errorf("failed to put file: %w", err)
}
}
return true, nil
}
func bucketAlreadyExistsErr(err error) bool {
resp := &minio.ErrorResponse{}
// https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
alreadyExistsCodes := map[string]bool{"BucketAlreadyExists": true, "BucketAlreadyOwnedByYou": true}
return errors.As(err, resp) && alreadyExistsCodes[resp.Code]
}
// ListObjects returns the files inside the directory represented by the Artifact
func (s3Driver *ArtifactDriver) ListObjects(ctx context.Context, artifact *wfv1.Artifact) ([]string, error) {View on GitHub (pinned to 35bff19146)
Solutions
- Retry the workflow step — transient errors are retried by the executor backoff
- Verify s3:PutObject (and s3:ListBucket) on the target bucket/prefix
- Check MinIO server disk/capacity if using MinIO
- Increase artifact bandwidth/reduce artifact size or split the directory
- Check the endpoint network path (DNS, TLS, proxies) from the workflow pod
Defensive patterns
Strategy: retry
Validate before calling
aws s3api head-bucket --bucket my-bucket && aws s3api put-object --bucket my-bucket --key .probe --body /dev/null
Try / catch
err := s3Driver.Save(ctx, path, art)
if err != nil {
if isTransientS3Err(ctx, err) {
// executor already retried; surface or re-run the node
}
return fmt.Errorf("artifact upload failed: %w", err)
} Prevention
- Grant s3:PutObject on the artifact prefix
- Keep artifacts modest in size; tar/compress large directories
- Monitor MinIO capacity and S3 5xx/throttle metrics
- Ensure stable network path from pods to the endpoint (no flaky DNS/proxy)
When it happens
Trigger: saveS3Artifact with isDir=true: s3cli.PutDirectory(bucket, key, path) failed — transient S3 error (throttle/network), access denied on PutObject, multipart upload failure on large trees, or prefix name conflicts.
Common situations: Uploading large artifact directories that hit timeouts/rate limits; IAM lacking s3:PutObject; MinIO disk full server-side; flaky network between executor and endpoint causing repeated retries that eventually give up.
Related errors
- failed to get directory: %w
- failed to put file: %w
- failed to check if key %s exists from bucket %s: %w
- Failed to save artifact: %v
- failed to stream artifact: %v
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/f636eb03ce7d8302.
Report an issue: GitHub.