{"record":{"id":"a2a95086c0e388fb","repo":"argoproj/argo-workflows","slug":"unable-to-determine-if-s-is-a-directory-w","errorCode":null,"errorMessage":"unable to determine if %s is a directory: %w","messagePattern":"unable to determine if (.+?) is a directory: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/artifacts/azure/azure.go","lineNumber":147,"sourceCode":"\t\tfileInfo, lstatErr := os.Lstat(path)\n\t\tif lstatErr != nil {\n\t\t\treturn fmt.Errorf(\"unable to retrieve stats for downloaded file %s: %w\", path, lstatErr)\n\t\t}\n\n\t\t// Empty file means it could be an ADLS Gen 2 account and we downloaded the\n\t\t// directory as an empty file -- we'll check below. If it's a non-empty file,\n\t\t// then we successfully downloaded a file blob.\n\t\tif fileInfo.Size() > 0 {\n\t\t\treturn nil\n\t\t}\n\t\tisEmptyFile = true\n\t} else if !bloberror.HasCode(origErr, bloberror.BlobNotFound) {\n\t\treturn fmt.Errorf(\"unable to download blob %s: %w\", artifact.Azure.Blob, origErr)\n\t}\n\n\tisDir, err := azblobDriver.IsDirectory(ctx, artifact)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to determine if %s is a directory: %w\", artifact.Azure.Blob, err)\n\t}\n\n\t// It's not a directory and the file doesn't exist, Return the original NoSuchKey error.\n\tif !isDir && !isEmptyFile {\n\t\treturn argoerrors.New(argoerrors.CodeNotFound, origErr.Error())\n\t}\n\n\t// When we tried to download the blob as a file, we created an empty file for the\n\t// blob as a target. We need to delete that empty file so we can re-create as a directory.\n\terr = os.Remove(path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to remove attempted file download %s: %w\", path, err)\n\t}\n\n\t// It's a directory, so download all of the files.\n\terr = azblobDriver.DownloadDirectory(ctx, containerClient, artifact, path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to download directory %s: %w\", artifact.Azure.Blob, err)","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/artifacts/azure/azure.go#L129-L165","documentation":"During Load, after a BlobNotFound (or empty ADLS Gen2 file), the driver calls IsDirectory, which lists blobs under the key's prefix. If that list call fails, the wrapped error is returned here. It hides the actual list failure (auth, container not found, network) behind the directory probe.","triggerScenarios":"DownloadFile returned BlobNotFound (or empty file) and then containerClient.NewListBlobsFlatPager/NextPage in IsDirectory fails: 403 AuthorizationFailure on list, ContainerNotFound, invalid SharedKey/SAS signature on list operations, transient network failure.","commonSituations":"SAS token lacking List permission (only Read), account key valid for get but firewall/rate limits hitting list calls, misconfigured endpoint so list goes to wrong account, HNS-enabled accounts with permission differences between read and list.","solutions":["Inspect the wrapped error code: grant the credential Storage Blob Data Reader (includes List) if it's a 403.","If using SAS, regenerate it with List ('l') permission included.","Confirm the container name in the artifact is correct; a wrong container fails list operations.","Retry the workflow on transient (network/timeout) wrapped errors.","For ADLS Gen2, verify RBAC on the storage account allows directory listing, not just blob read."],"exampleFix":"// before: SAS without list permission\naz storage blob generate-sas --permissions r ...\n// after: include list permission\naz storage blob generate-sas --permissions rl ...","handlingStrategy":"try-catch","validationCode":"// verify list permission before relying on directory artifacts\npager := containerClient.NewListBlobsFlatOptions(&azblob.ListBlobsFlatOptions{Prefix: &prefix})\nif _, err := pager.NextPage(ctx); err != nil { return err }","typeGuard":"func isListAuthFailure(err error) bool {\n\tvar respErr *azcore.ResponseError\n\treturn errors.As(err, &respErr) && respErr.ErrorCode == \"AuthorizationFailure\"\n}","tryCatchPattern":"err := driver.Load(ctx, artifact, path)\nif strings.Contains(err.Error(), \"unable to determine if\") {\n\tvar respErr *azcore.ResponseError\n\tif errors.As(err, &respErr) && respErr.ErrorCode == \"AuthorizationFailure\" {\n\t\t// grant List permission (Storage Blob Data Reader or SAS 'l')\n\t}\n}","preventionTips":["Generate SAS tokens with both read and list permissions ('rl')","Assign Storage Blob Data Reader role rather than raw key-only Read scoping","Test directory artifacts with a list call before production","Keep container names verified via az storage container list"],"tags":["azure","blob-storage","list","permissions"],"backgroundTag":"azure-blob-list-failed","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"}