argoproj/argo-workflows · error

unable to create file %s: %w

Error message

unable to create file %s: %w

What it means

DownloadFile creates (or truncates) the local destination file with os.Create before streaming the blob into it. This error means os.Create failed, so nothing was downloaded. Typical causes are local filesystem problems at the exact destination path, not Azure-side issues.

Source

Thrown at workflow/artifacts/azure/azure.go:181

	err = azblobDriver.DownloadDirectory(ctx, containerClient, artifact, path)
	if err != nil {
		return fmt.Errorf("unable to download directory %s: %w", artifact.Azure.Blob, err)
	}

	return nil
}

// DownloadFile downloads a single file from Azure Blob Storage
func DownloadFile(ctx context.Context, containerClient *container.Client, blobName, path string) error {
	blobClient := containerClient.NewBlobClient(blobName)

	err := os.MkdirAll(filepath.Dir(path), 0755)
	if err != nil {
		return fmt.Errorf("unable to create dir for file %s: %w", path, err)
	}
	outFile, err := os.Create(path)
	if err != nil {
		return fmt.Errorf("unable to create file %s: %w", path, err)
	}
	defer func() {
		if closeErr := outFile.Close(); closeErr != nil {
			logger := logging.RequireLoggerFromContext(ctx)
			logger.WithFatal().WithError(closeErr).Warn(ctx, "unable to close file")
		}
	}()

	_, err = blobClient.DownloadFile(ctx, outFile, nil)
	return err
}

// DownloadDirectory downloads all of the files starting with the named blob prefix into a local directory.
func (azblobDriver *ArtifactDriver) DownloadDirectory(ctx context.Context, containerClient *container.Client, artifact *wfv1.Artifact, path string) error {
	logger := logging.RequireLoggerFromContext(ctx)
	logger.WithField("endpoint", artifact.Azure.Endpoint).
		WithField("container", artifact.Azure.Container).
		WithField("blob", artifact.Azure.Blob).

View on GitHub (pinned to 35bff19146)

Solutions

  1. Check the wrapped syscall: EACCES => fix permissions/securityContext; EISDIR => choose a file path; ENOSPC => free disk or expand the volume.
  2. Ensure the artifact path points to a file location writable by argoexec, not a directory.
  3. Free up disk on the node/emptyDir if ENOSPC.
  4. Shorten blob names/prefixes for directory artifacts if ENAMETOOLONG.
  5. Verify the mount backing the path is rw (kubectl describe pod mounts).

Example fix

// before: path is a directory
path: /mnt/out
// after
path: /mnt/out/data.txt
Defensive patterns

Strategy: validation

Validate before calling

if fi, err := os.Stat(path); err == nil && fi.IsDir() {
	return fmt.Errorf("%s is a directory, expected a file path", path)
}
if err := unix.Access(filepath.Dir(path), unix.W_OK); err != nil {
	return fmt.Errorf("no write access to %s", filepath.Dir(path))
}

Type guard

func canCreateFile(p string) error {
	if fi, err := os.Stat(p); err == nil && fi.IsDir() {
		return fmt.Errorf("%s is a directory", p)
	}
	f, err := os.OpenFile(p, os.O_CREATE|os.O_WRONLY, 0o644)
	if err != nil { return err }
	return f.Close()
}

Prevention

When it happens

Trigger: os.Create(path) fails with EACCES (no write permission), EISDIR (path is an existing directory), ENOSPC (disk full), ENAMETOOLONG, or the volume is read-only.

Common situations: Artifact destination path collides with an existing directory, container disk quota/full ephemeral storage, executor running as non-root against root-owned mount, overly long path from deep blob-name prefixes in directory artifacts.

Related errors


AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03). Data as JSON: /api/errors/10ab6d2d6f1a5120. Report an issue: GitHub.