argoproj/argo-workflows · error
failed to test if %s is a directory: %w
Error message
failed to test if %s is a directory: %w
What it means
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.
Source
Thrown at workflow/artifacts/azure/azure.go:284
// Not a directory (and not successful retrieval of an empty file), so return
// the original BlobNotFound error
return nil, fmt.Errorf("unable to open blob stream for %s: %w", artifact.Azure.Blob, origErr)
}
return response.Body, nil
}
// Save saves an artifact to Azure Blob Storage
func (azblobDriver *ArtifactDriver) Save(ctx context.Context, path string, outputArtifact *wfv1.Artifact) error {
logger := logging.RequireLoggerFromContext(ctx)
logger.WithField("endpoint", outputArtifact.Azure.Endpoint).
WithField("container", outputArtifact.Azure.Container).
WithField("blob", outputArtifact.Azure.Blob).
Info(ctx, "Saving to Azure Blob Storage")
isDir, err := file.IsDirectory(path)
if err != nil {
return fmt.Errorf("failed to test if %s is a directory: %w", path, err)
}
containerClient, err := azblobDriver.newAzureContainerClient(ctx)
if err != nil {
return fmt.Errorf("unable to create Azure Blob Container client for %s: %w", outputArtifact.Azure.Blob, err)
}
if isDir {
err := PutDirectory(ctx, containerClient, outputArtifact.Azure.Blob, path)
if err != nil {
return fmt.Errorf("unable to upload directory %s to Azure: %w", path, err)
}
} else {
err := PutFile(ctx, containerClient, outputArtifact.Azure.Blob, path)
if err != nil {
return fmt.Errorf("unable to upload file %s to Azure: %w", path, err)
}
}View on GitHub (pinned to 35bff19146)
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.
Example fix
// before
outputs:
artifacts:
- {name: out, path: /mnt/out/results.tgz} # file written to /work/results.tgz
// after
outputs:
artifacts:
- {name: out, path: /work/results.tgz} Defensive patterns
Strategy: validation
Validate before calling
if _, err := os.Stat(path); err != nil {
return fmt.Errorf("artifact path %s missing before save: %w", path, err)
} Type guard
func pathExists(p string) bool {
_, err := os.Stat(p)
return !errors.Is(err, os.ErrNotExist)
} Try / catch
err := driver.Save(ctx, path, artifact)
if err != nil {
var pe *fs.PathError
if errors.As(err, &pe) && errors.Is(pe.Err, os.ErrNotExist) {
// producer step didn't write the file
}
return err
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- unable to retrieve stats for downloaded file %s: %w
- unable to remove attempted file download %s: %w
- unable to open stream for blob %s: %w
- unable to test if blob %s is a directory: %w
- unable to open blob stream for %s: %w
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/ef72b50c2fb7a5ca.
Report an issue: GitHub.