{"record":{"id":"2591101eb674b7a5","repo":"argoproj/argo-workflows","slug":"unable-to-upload-directory-s-to-azure-w","errorCode":null,"errorMessage":"unable to upload directory %s to Azure: %w","messagePattern":"unable to upload directory (.+?) to Azure: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/artifacts/azure/azure.go","lineNumber":295,"sourceCode":"\tlogger.WithField(\"endpoint\", outputArtifact.Azure.Endpoint).\n\t\tWithField(\"container\", outputArtifact.Azure.Container).\n\t\tWithField(\"blob\", outputArtifact.Azure.Blob).\n\t\tInfo(ctx, \"Saving to Azure Blob Storage\")\n\n\tisDir, err := file.IsDirectory(path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to test if %s is a directory: %w\", path, err)\n\t}\n\n\tcontainerClient, err := azblobDriver.newAzureContainerClient(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to create Azure Blob Container client for %s: %w\", outputArtifact.Azure.Blob, err)\n\t}\n\n\tif isDir {\n\t\terr := PutDirectory(ctx, containerClient, outputArtifact.Azure.Blob, path)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"unable to upload directory %s to Azure: %w\", path, err)\n\t\t}\n\t} else {\n\t\terr := PutFile(ctx, containerClient, outputArtifact.Azure.Blob, path)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"unable to upload file %s to Azure: %w\", path, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// SaveStream saves an artifact from an io.Reader to Azure Blob Storage\nfunc (azblobDriver *ArtifactDriver) SaveStream(ctx context.Context, reader io.Reader, outputArtifact *wfv1.Artifact) error {\n\tlogger := logging.RequireLoggerFromContext(ctx)\n\tlogger.WithField(\"endpoint\", outputArtifact.Azure.Endpoint).\n\t\tWithField(\"container\", outputArtifact.Azure.Container).\n\t\tWithField(\"blob\", outputArtifact.Azure.Blob).\n\t\tInfo(ctx, \"Streaming to Azure Blob Storage\")","sourceCodeStart":277,"sourceCodeEnd":313,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/artifacts/azure/azure.go#L277-L313","documentation":"Save uploads a whole directory via PutDirectory, which walks the tree and calls PutFile per entry. Any per-file upload failure (auth, throttling, network, blob-name issues) surfaces as this wrapped error.","triggerScenarios":"Saving a directory artifact when one of the recursive PutFile uploads fails: Azure authorization errors, upload stream failures, invalid blob names derived from odd file paths, request timeouts on large trees.","commonSituations":"Uploading directories with thousands of files and hitting throttling; file names with characters Azure rejects; intermittent network drops in-cluster; key rotation mid-upload.","solutions":["Look at the inner PutFile error for the specific file and Azure error code.","Retry the workflow if the failure was transient (429/503/timeout).","Reduce artifact size/count or increase timeouts; avoid saving huge trees as artifacts.","Ensure account key permissions allow blob writes (Storage Blob Data Contributor).","Sanitize unusual characters in file names before they become blob keys."],"exampleFix":"// before: saving a node_modules-style huge dir as artifact\nartifacts: [{name: workdir, path: /work}]\n// after: exclude volatile dirs or archive first\nartifacts: [{name: workdir, path: /work/archive.tgz}]","handlingStrategy":"retry","validationCode":"count := 0\nfilepath.WalkDir(path, func(p string, d fs.DirEntry, err error) error {\n  count++\n  return nil\n})\nif count > 10000 {\n  return fmt.Errorf(\"refusing to save %d files as one artifact\", count)\n}","typeGuard":"func isThrottled(err error) bool {\n  var re *azcore.ResponseError\n  return errors.As(err, &re) && re.StatusCode == 429\n}","tryCatchPattern":"err := driver.Save(ctx, dir, artifact)\nif err != nil && strings.Contains(err.Error(), \"unable to upload directory\") {\n  if isThrottled(errors.Unwrap(err)) {\n    // backoff and retry\n  }\n  return err\n}","preventionTips":["Archive large directory trees into a single file before saving.","Exclude dependency/volatile directories from artifact paths.","Prefer retry with backoff for large multi-file uploads.","Keep blob-name-safe file names (avoid reserved/odd characters)."],"tags":["azure","blob-storage","upload","artifact"],"backgroundTag":"azure-blob-upload-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"}