{"record":{"id":"16c3d0e3c69abc2e","repo":"argoproj/argo-workflows","slug":"unable-to-download-file-s-w","errorCode":null,"errorMessage":"unable to download file %s: %w","messagePattern":"unable to download file (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/artifacts/azure/azure.go","lineNumber":223,"sourceCode":"\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 {\n\t\t\treturn fmt.Errorf(\"unable to download file %s: %w\", localPath, err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// OpenStream opens a stream reader for an artifact from Azure Blob Storage\nfunc (azblobDriver *ArtifactDriver) OpenStream(ctx context.Context, artifact *wfv1.Artifact) (io.ReadCloser, 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, \"Streaming from Azure Blob Storage\")\n\tcontainerClient, err := azblobDriver.newAzureContainerClient(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to create Azure Blob Container client: %w\", err)\n\t}\n\n\tblobClient := containerClient.NewBlockBlobClient(artifact.Azure.Blob)","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/artifacts/azure/azure.go#L205-L241","documentation":"While downloading a directory artifact, each child blob is fetched with DownloadFile into its mapped local path; this error wraps the failure of one specific child download. It is the per-file counterpart of the directory download — the nested error carries the actual cause (Azure-side or local FS).","triggerScenarios":"DownloadFile(childBlob, localPath) fails: child blob deleted between list and download (BlobNotFound), 403 on that blob, transient network failure mid-stream, or local os.Create/MkdirAll failure at the derived localPath (permissions, disk full, illegal filename from blob name).","commonSituations":"Blob names with characters invalid on Linux paths in directory artifacts, concurrent producer writing/deleting blobs while the workflow downloads, ephemeral disk filling during large directory downloads, RBAC that allows some blobs but not others (per-path policies in ADLS Gen2).","solutions":["Read the nested wrapped error: Azure code => fix access/re-upload that blob; os error => fix local path/permissions/disk.","Re-upload the directory artifact to ensure all children exist and are consistent.","If a blob name contains path-hostile characters, sanitize names when saving the artifact.","Increase disk/emptyDir size if ENOSPC occurred mid-download.","Retry the workflow — mid-download transient network failures are often one-shot."],"exampleFix":"// before: producer deletes blobs during download\nstep writes output -> workflow downloads same prefix concurrently\n// after: snapshot first (copy or version prefix)\nupload to blob: outputs/run-42/ then download outputs/run-42/","handlingStrategy":"retry","validationCode":"// check disk headroom before bulk download\nvar st syscall.Statfs_t\nif err := syscall.Statfs(filepath.Dir(path), &st); err == nil {\n\tavail := int64(st.Bavail) * int64(st.Bsize)\n\tif avail < minRequiredBytes { return fmt.Errorf(\"insufficient disk: %d\", avail) }\n}","typeGuard":"func isRetryableDownloadErr(err error) bool {\n\tvar respErr *azcore.ResponseError\n\tif errors.As(err, &respErr) {\n\t\treturn respErr.StatusCode == 500 || respErr.StatusCode == 503 || respErr.StatusCode == 504\n\t}\n\treturn errors.Is(err, context.DeadlineExceeded) || errors.Is(err, io.ErrUnexpectedEOF)\n}","tryCatchPattern":"err := driver.Load(ctx, artifact, path)\nif err != nil {\n\tif isRetryableDownloadErr(err) {\n\t\ttime.Sleep(backoff)\n\t\terr = driver.Load(ctx, artifact, path)\n\t}\n}","preventionTips":["Freeze the source prefix (versioned directories) before downloading","Provision adequate emptyDir/PVC size for directory artifacts","Sanitize blob names when saving so derived local paths are valid","Retry transient errors; investigate nested errors otherwise"],"tags":["azure","blob-storage","directory","download"],"backgroundTag":"azure-blob-download-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"}