argoproj/argo-workflows · error
unable to list files in %s: %w
Error message
unable to list files in %s: %w
What it means
Delete of an artifact whose key ends with '/' is treated as a directory: the driver lists all objects under the prefix before deleting them. This error wraps a ListDirectory failure, so nothing was deleted.
Source
Thrown at workflow/artifacts/s3/s3.go:312
defer cancel()
log := logging.RequireLoggerFromContext(ctx)
err := retry.OnError(retry.DefaultBackoff, func(err error) bool {
return isTransientS3Err(ctx, err)
}, func() error {
log.WithField("key", artifact.S3.Key).Info(ctx, "S3 Delete")
s3cli, err := s3Driver.newClient(ctx)
if err != nil {
return err
}
// check suffix instead of s3cli.IsDirectory as it requires another request for file delete (most scenarios)
if !strings.HasSuffix(artifact.S3.Key, "/") {
return s3cli.Delete(artifact.S3.Bucket, artifact.S3.Key)
}
keys, err := s3cli.ListDirectory(artifact.S3.Bucket, artifact.S3.Key)
if err != nil {
return fmt.Errorf("unable to list files in %s: %w", artifact.S3.Key, err)
}
for _, objKey := range keys {
err = s3cli.Delete(artifact.S3.Bucket, objKey)
if err != nil {
return err
}
}
return nil
})
return err
}
// saveS3Artifact uploads artifacts to an S3 compliant storage
// returns true if the upload is completed or can't be retried (non-transient error)
// returns false if it can be retried (transient error)
func saveS3Artifact(ctx context.Context, s3cli Client, path string, outputArtifact *wfv1.Artifact) (bool, error) {
isDir, err := file.IsDirectory(path)View on GitHub (pinned to 35bff19146)
Solutions
- Grant s3:ListBucket to the deleting principal's credentials
- Retry — Delete already retries on transient errors with DefaultBackoff
- If the key is actually a single object, remove the trailing '/' from the artifact key so the simple Delete path is used
- Check endpoint compatibility (ListObjectsV2) for MinIO/GCS-interop
Example fix
// before: key treated as directory key: "my/artifacts/" // after (single object) key: "my/artifacts"
Defensive patterns
Strategy: validation
Validate before calling
// before deleting a directory artifact, confirm list access
keys, err := s3cli.ListDirectory(bucket, key)
if err != nil { return fmt.Errorf("cannot list prefix %s: %w", key, err) } Prevention
- Grant s3:ListBucket wherever directory artifact deletion is enabled
- Only append '/' to keys that are genuine prefixes
- Handle Delete results in archive GC with retries and alerting
- Test deletion against MinIO/GCS-interop before production
When it happens
Trigger: ArtifactDriver.Delete called with artifact.S3.Key ending in '/': s3cli.ListDirectory(bucket, key) failed — ListBucket permission denied, throttling, network error, or endpoint incompatibility.
Common situations: Deleting archived directory artifacts with credentials lacking s3:ListBucket; garbage-collection of large prefixes hitting list pagination limits or rate limits; S3-compatible providers with listing quirks.
Related errors
- failed to get file: %w
- failed to test if %s is a directory: %w
- failed to create bucket %s: %w
- unable to delete Azure Blob %s: %w
- delete %s: %w
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/fcbde5a727d64fdb.
Report an issue: GitHub.