argoproj/argo-workflows · error
failed to create directory
Error message
failed to create directory %s: %w
What it means
saveArtifact failed to create the parent directory of the artifact destination file under /var/run/argo/outputs/artifacts/ via os.MkdirAll(0o755). The artifact cannot be exported, so the step's output artifact will be missing. The wrapped error reveals the OS cause (usually permissions or disk space).
Solutions
- Read the wrapped error: EEXIST/ENOTDIR means a file occupies a directory component of the artifact path — rename the template's artifact path.
- Free node disk space if ENOSPC.
- Grant the container write access to /var/run/argo (volume mount, correct runAsUser, not read-only).
- Verify the artifact path in the template is a valid directory-form path (path.Dir is computed on it).
Example fix
// before: template artifact path that collides with a file
outputs:
artifacts:
- name: out
path: /tmp/result # /tmp/result is a FILE being both source base and dir component
// after: use a dedicated output directory path
outputs:
artifacts:
- name: out
path: /tmp/results/output.tgz Defensive patterns
Strategy: validation
Validate before calling
// validate template artifact paths before submit
for _, art := range tmpl.Outputs.Artifacts {
if strings.HasSuffix(art.Path, "/") || art.Path == "" {
return fmt.Errorf("artifact %q path %q invalid", art.Name, art.Path)
}
}
// ensure /var/run/argo/outputs is writable in the image
// os.MkdirAll("/var/run/argo/outputs/artifacts", 0o755) Try / catch
if err := runStep(ctx); err != nil {
var wrapped error
if errors.Unwrap(err) != nil {
wrapped = errors.Unwrap(err)
}
if errors.Is(wrapped, os.ErrExist) || errors.Is(wrapped, syscall.ENOTDIR) {
return fixArtifactPathAndResubmit(ctx)
}
return err
} Prevention
- Choose artifact output paths that don't collide with existing files
- Test templates with argo lint and a dry run in a namespace with matching securityContext
- Keep /var/run/argo on a writable emptyDir
- Watch node ENOSPC alerts before large fan-out workflows
When it happens
Trigger: os.MkdirAll(filepath.Dir(dstPath), 0o755) fails while saving an output artifact: /var/run/argo/outputs is not writable, disk is full, or a non-directory file exists at a path component of dstPath (path derived from the template's artifact path).
Common situations: Templates whose output artifact path collides with an existing file (e.g. path /var/run/argo/outputs/artifacts/foo where foo is a file); readOnlyRootFilesystem pods; ENOSPC; runAsUser without write access to /var/run/argo.
Related errors
- failed to close
- failed to create destination
- failed to clear existing path for artifact
- failed to copy to
- failed to create parent directory for artifact
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/1d5b901a29ffa1d0.
Report an issue: GitHub.
Appendix: source
Thrown at cmd/argoexec/commands/emissary.go:709
func saveArtifact(ctx context.Context, template *wfv1.Template, srcPath string) error {
logger := logging.RequireLoggerFromContext(ctx)
if common.FindOverlappingVolume(template, srcPath) != nil {
logger.WithField("srcPath", srcPath).Info(ctx, "no need to save artifact - on overlapping volume")
return nil
}
if _, err := os.Stat(srcPath); os.IsNotExist(err) { // might be optional, so we ignore
logger.WithField("srcPath", srcPath).WithError(err).Warn(ctx, "cannot save artifact")
return nil
}
dstPath := filepath.Join(varRunArgo, "/outputs/artifacts/", strings.TrimSuffix(srcPath, "/")+".tgz")
logger.WithFields(logging.Fields{
"src": srcPath,
"dst": dstPath,
}).Info(ctx, "saving artifact")
z := filepath.Dir(dstPath)
if err := os.MkdirAll(z, 0o755); err != nil { // chmod rwxr-xr-x
return fmt.Errorf("failed to create directory %s: %w", z, err)
}
dst, err := os.Create(dstPath)
if err != nil {
return fmt.Errorf("failed to create destination %s: %w", dstPath, err)
}
defer func() { _ = dst.Close() }()
if err = archive.TarGzToWriter(ctx, srcPath, gzip.DefaultCompression, dst); err != nil {
return fmt.Errorf("failed to tarball the output %s to %s: %w", srcPath, dstPath, err)
}
if err = dst.Close(); err != nil {
return fmt.Errorf("failed to close %s: %w", dstPath, err)
}
return nil
}
func saveParameter(ctx context.Context, template *wfv1.Template, srcPath string) error {
logger := logging.RequireLoggerFromContext(ctx)
View on GitHub (pinned to 35bff19146)