{"record":{"id":"fcbde5a727d64fdb","repo":"argoproj/argo-workflows","slug":"unable-to-list-files-in-s-w-fcbde5","errorCode":null,"errorMessage":"unable to list files in %s: %w","messagePattern":"unable to list files in (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/artifacts/s3/s3.go","lineNumber":312,"sourceCode":"\tdefer cancel()\n\tlog := logging.RequireLoggerFromContext(ctx)\n\terr := retry.OnError(retry.DefaultBackoff, func(err error) bool {\n\t\treturn isTransientS3Err(ctx, err)\n\t}, func() error {\n\t\tlog.WithField(\"key\", artifact.S3.Key).Info(ctx, \"S3 Delete\")\n\t\ts3cli, err := s3Driver.newClient(ctx)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// check suffix instead of s3cli.IsDirectory as it requires another request for file delete (most scenarios)\n\t\tif !strings.HasSuffix(artifact.S3.Key, \"/\") {\n\t\t\treturn s3cli.Delete(artifact.S3.Bucket, artifact.S3.Key)\n\t\t}\n\n\t\tkeys, err := s3cli.ListDirectory(artifact.S3.Bucket, artifact.S3.Key)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"unable to list files in %s: %w\", artifact.S3.Key, err)\n\t\t}\n\t\tfor _, objKey := range keys {\n\t\t\terr = s3cli.Delete(artifact.S3.Bucket, objKey)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n\n\treturn err\n}\n\n// saveS3Artifact uploads artifacts to an S3 compliant storage\n// returns true if the upload is completed or can't be retried (non-transient error)\n// returns false if it can be retried (transient error)\nfunc saveS3Artifact(ctx context.Context, s3cli Client, path string, outputArtifact *wfv1.Artifact) (bool, error) {\n\tisDir, err := file.IsDirectory(path)","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/artifacts/s3/s3.go#L294-L330","documentation":"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.","triggerScenarios":"ArtifactDriver.Delete called with artifact.S3.Key ending in '/': s3cli.ListDirectory(bucket, key) failed — ListBucket permission denied, throttling, network error, or endpoint incompatibility.","commonSituations":"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.","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"],"exampleFix":"// before: key treated as directory\nkey: \"my/artifacts/\"\n// after (single object)\nkey: \"my/artifacts\"","handlingStrategy":"validation","validationCode":"// before deleting a directory artifact, confirm list access\nkeys, err := s3cli.ListDirectory(bucket, key)\nif err != nil { return fmt.Errorf(\"cannot list prefix %s: %w\", key, err) }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["s3","delete","aws","permissions"],"backgroundTag":"s3-access-denied","analyzedSha":"35bff19146f5a6ada77468c431f2624bd577e373","analyzedAt":"2026-09-03T19:34:35.908Z","contentChangedAt":"2026-09-03T19:34:35.908Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}