{"record":{"id":"ef72b50c2fb7a5ca","repo":"argoproj/argo-workflows","slug":"failed-to-test-if-s-is-a-directory-w","errorCode":null,"errorMessage":"failed to test if %s is a directory: %w","messagePattern":"failed to test if (.+?) is a directory: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/artifacts/azure/azure.go","lineNumber":284,"sourceCode":"\t\t// Not a directory (and not successful retrieval of an empty file), so return\n\t\t// the original BlobNotFound error\n\t\treturn nil, fmt.Errorf(\"unable to open blob stream for %s: %w\", artifact.Azure.Blob, origErr)\n\t}\n\n\treturn response.Body, nil\n}\n\n// Save saves an artifact to Azure Blob Storage\nfunc (azblobDriver *ArtifactDriver) Save(ctx context.Context, path string, 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, \"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}","sourceCodeStart":266,"sourceCodeEnd":302,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/artifacts/azure/azure.go#L266-L302","documentation":"Save first checks with file.IsDirectory whether the local artifact path is a directory, to choose PutDirectory vs PutFile. If os.Stat on the path fails, the stat error is wrapped with this message. It almost always means the local file/directory that should have been saved does not exist.","triggerScenarios":"Save(path) where path was never created by the step: wrong template 'path' for the output artifact, step script wrote to a different location, or volume mount missing.","commonSituations":"Output artifact path typo (e.g. /mnt/out vs /mnt/outputs); script failed silently before writing the file; artifact path outside the mounted volume; optional artifact not configured as optional.","solutions":["Verify the output artifact 'path' matches where the step actually writes (ls the mount to confirm).","Make the step fail loudly if the file wasn't produced, or mark the artifact optional.","Ensure the path is inside a mounted volume shared between containers in the pod.","Check container working directory assumptions — use absolute paths.","Inspect earlier step logs for a failure that prevented file creation."],"exampleFix":"// before\noutputs:\n  artifacts:\n    - {name: out, path: /mnt/out/results.tgz}   # file written to /work/results.tgz\n// after\noutputs:\n  artifacts:\n    - {name: out, path: /work/results.tgz}","handlingStrategy":"validation","validationCode":"if _, err := os.Stat(path); err != nil {\n  return fmt.Errorf(\"artifact path %s missing before save: %w\", path, err)\n}","typeGuard":"func pathExists(p string) bool {\n  _, err := os.Stat(p)\n  return !errors.Is(err, os.ErrNotExist)\n}","tryCatchPattern":"err := driver.Save(ctx, path, artifact)\nif err != nil {\n  var pe *fs.PathError\n  if errors.As(err, &pe) && errors.Is(pe.Err, os.ErrNotExist) {\n    // producer step didn't write the file\n  }\n  return err\n}","preventionTips":["Use absolute artifact paths inside mounted volumes.","Make scripts exit non-zero if expected outputs are not created.","Smoke-test templates with `argo lint` and a dry-run step listing outputs.","Avoid shared-volume races between cleanup and artifact save."],"tags":["filesystem","artifact","azure","path"],"backgroundTag":"file-not-found","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"}