{"record":{"id":"0a1c6917f6b476ac","repo":"argoproj/argo-workflows","slug":"unable-to-list-blob-s-in-azure-storage-w","errorCode":null,"errorMessage":"unable to list blob %s in Azure Storage: %w","messagePattern":"unable to list blob (.+?) in Azure Storage: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/artifacts/azure/azure.go","lineNumber":204,"sourceCode":"\t\t\tlogger.WithFatal().WithError(closeErr).Warn(ctx, \"unable to close file\")\n\t\t}\n\t}()\n\n\t_, err = blobClient.DownloadFile(ctx, outFile, nil)\n\treturn err\n}\n\n// DownloadDirectory downloads all of the files starting with the named blob prefix into a local directory.\nfunc (azblobDriver *ArtifactDriver) DownloadDirectory(ctx context.Context, containerClient *container.Client, artifact *wfv1.Artifact, path string) error {\n\tlogger := logging.RequireLoggerFromContext(ctx)\n\tlogger.WithField(\"endpoint\", artifact.Azure.Endpoint).\n\t\tWithField(\"container\", artifact.Azure.Container).\n\t\tWithField(\"blob\", artifact.Azure.Blob).\n\t\tInfo(ctx, \"Downloading directory from Azure Blob Storage\")\n\n\tfiles, err := azblobDriver.ListObjects(ctx, artifact)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to list blob %s in Azure Storage: %w\", artifact.Azure.Blob, err)\n\t}\n\n\terr = os.MkdirAll(path, 0755)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to create local directory %s: %w\", path, err)\n\t}\n\n\tfor _, file := range files {\n\t\t// For ADLS Gen 2 accounts, we'll see a file whose name matches the directory. Skip it.\n\t\tif file == artifact.Azure.Blob {\n\t\t\tcontinue\n\t\t}\n\n\t\trelKeyPath := strings.TrimPrefix(file, artifact.Azure.Blob)\n\t\tlocalPath := filepath.Join(path, relKeyPath)\n\n\t\terr = DownloadFile(ctx, containerClient, file, localPath)\n\t\tif err != nil {","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/artifacts/azure/azure.go#L186-L222","documentation":"DownloadDirectory lists all blobs under the artifact's blob prefix via ListObjects before downloading them. This error wraps a failure from that listing — either the container client creation inside ListObjects or a pager.NextPage API failure. The real cause is in the wrapped error.","triggerScenarios":"ListObjects' list call fails: 403 AuthorizationFailure (SAS/identity lacks List), ContainerNotFound (wrong container name), InvalidQueryParameterValue/other REST errors, context deadline exceeded on huge prefixes, or newAzureContainerClient fails (bad endpoint/key).","commonSituations":"SAS token generated with only read ('r') but not list ('l') permission, typo in artifact.Azure.Container, storage account firewall blocking the cluster egress, listing a prefix with millions of blobs until timeout.","solutions":["Check the wrapped error code: 403 => grant List (Storage Blob Data Reader or SAS 'l' permission); 404 container => fix container name.","Verify endpoint and credentials by running az storage blob list with the same key locally.","If the wrapped error is a client-creation error, fix the endpoint/accountKey per the nested message.","Retry on transient network/timeout errors; narrow the artifact prefix to reduce listing size.","Ensure network egress from workflow pods to <account>.blob.core.windows.net:443 is allowed."],"exampleFix":"// before\npermissions: r\n// after: SAS with list\naz storage blob generate-sas --permissions rl --https-only ...","handlingStrategy":"try-catch","validationCode":"// pre-flight: list with the same prefix before running the workflow\n_, err := containerClient.NewListBlobsFlatPager(&azblob.ListBlobsFlatOptions{Prefix: &prefix}).NextPage(ctx)\nif err != nil { return fmt.Errorf(\"list precheck failed: %w\", err) }","typeGuard":"func isSasMissingListPerm(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 list blob\") {\n\tvar respErr *azcore.ResponseError\n\tif errors.As(err, &respErr) {\n\t\tif respErr.ErrorCode == \"AuthorizationFailure\" { /* add List permission */ }\n\t\tif respErr.StatusCode >= 500 || respErr.StatusCode == 408 { /* retry */ }\n\t}\n}","preventionTips":["Generate SAS tokens with 'rl' permissions","Verify container names with az CLI before submitting","Allow egress to *.blob.core.windows.net:443","Narrow artifact prefixes so listings stay small"],"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"}