argoproj/argo-workflows · error

unable to retrieve stats for downloaded file %s: %w

Error message

unable to retrieve stats for downloaded file %s: %w

What it means

After DownloadFile succeeds in Load, the driver calls os.Lstat(path) to determine the blob's size (an empty file may represent a downloaded ADLS Gen2 directory); if the stat fails — path missing, permission denied, or filesystem error — this wrapped error is returned.

Source

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

		WithField("blob", artifact.Azure.Blob).
		Info(ctx, "Downloading from Azure Blob Storage")
	containerClient, err := azblobDriver.newAzureContainerClient(ctx)
	if err != nil {
		return fmt.Errorf("unable to create Azure Blob Container client: %w", err)
	}

	// Assume we're not downloading a directory and try to download as a file, since this is
	// the most common case and we don't want the penalty of listing the blobs before we
	// download (to determine if it's a directory instead of a single file). If we get a
	// BlobNotFound error, then check if it's a directory and process accordingly. If the account
	// has HNS enabled (ADLS Gen 2), then there's an edge case with using the blob API to
	// access. The directory will be returned as an empty file, so check for that as well.
	var isEmptyFile bool
	origErr := DownloadFile(ctx, containerClient, artifact.Azure.Blob, path)
	if origErr == nil {
		fileInfo, lstatErr := os.Lstat(path)
		if lstatErr != nil {
			return fmt.Errorf("unable to retrieve stats for downloaded file %s: %w", path, lstatErr)
		}

		// Empty file means it could be an ADLS Gen 2 account and we downloaded the
		// directory as an empty file -- we'll check below. If it's a non-empty file,
		// then we successfully downloaded a file blob.
		if fileInfo.Size() > 0 {
			return nil
		}
		isEmptyFile = true
	} else if !bloberror.HasCode(origErr, bloberror.BlobNotFound) {
		return fmt.Errorf("unable to download blob %s: %w", artifact.Azure.Blob, origErr)
	}

	isDir, err := azblobDriver.IsDirectory(ctx, artifact)
	if err != nil {
		return fmt.Errorf("unable to determine if %s is a directory: %w", artifact.Azure.Blob, err)
	}

View on GitHub (pinned to 35bff19146)

Solutions

  1. Inspect the wrapped lstatErr cause to identify ENOENT vs EACCES vs EIO.
  2. Check the artifact path volume is writable and not concurrently cleaned (no conflicting sidecars/scanners).
  3. Re-run the workflow — a transient node/filesystem issue often resolves on retry.
  4. Verify securityContext/fsGroup settings allow the executor user to stat files under /tmp (or the configured artifact path).
Defensive patterns

Strategy: retry

Validate before calling

// ensure the artifact dir is writable before download:
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { return err }
if f, err := os.CreateTemp(filepath.Dir(path), ".probe"); err == nil { f.Close(); os.Remove(f.Name()) }

Try / catch

if err := DownloadFile(ctx, client, blob, path); err == nil {
	_, lstatErr := os.Lstat(path)
	if lstatErr != nil {
		if os.IsNotExist(lstatErr) || os.IsPermission(lstatErr) {
			return retryDownload(ctx, client, blob, path) // transient/perm issue
		}
		return fmt.Errorf("lstat %s: %w", path, lstatErr)
	}
}

Prevention

When it happens

Trigger: The downloaded file disappeared between download and stat (race, cleanup), the mount holding the artifact path is read-only or full, or the executor lacks permission to stat the file it just wrote on a volume with restrictive permissions.

Common situations: Running with securityContext that restricts filesystem access on the emptyDir/hostPath artifact volume; node disk pressure; a security scanner or sidecar deleting files in the artifacts directory mid-workflow.

Related errors


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