argoproj/argo-workflows · error

unable to determine if %s is a directory: %w

Error message

unable to determine if %s is a directory: %w

What it means

During Load, after a BlobNotFound (or empty ADLS Gen2 file), the driver calls IsDirectory, which lists blobs under the key's prefix. If that list call fails, the wrapped error is returned here. It hides the actual list failure (auth, container not found, network) behind the directory probe.

Source

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

		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)
	}

	// It's not a directory and the file doesn't exist, Return the original NoSuchKey error.
	if !isDir && !isEmptyFile {
		return argoerrors.New(argoerrors.CodeNotFound, origErr.Error())
	}

	// When we tried to download the blob as a file, we created an empty file for the
	// blob as a target. We need to delete that empty file so we can re-create as a directory.
	err = os.Remove(path)
	if err != nil {
		return fmt.Errorf("unable to remove attempted file download %s: %w", path, err)
	}

	// It's a directory, so download all of the files.
	err = azblobDriver.DownloadDirectory(ctx, containerClient, artifact, path)
	if err != nil {
		return fmt.Errorf("unable to download directory %s: %w", artifact.Azure.Blob, err)

View on GitHub (pinned to 35bff19146)

Solutions

  1. Inspect the wrapped error code: grant the credential Storage Blob Data Reader (includes List) if it's a 403.
  2. If using SAS, regenerate it with List ('l') permission included.
  3. Confirm the container name in the artifact is correct; a wrong container fails list operations.
  4. Retry the workflow on transient (network/timeout) wrapped errors.
  5. For ADLS Gen2, verify RBAC on the storage account allows directory listing, not just blob read.

Example fix

// before: SAS without list permission
az storage blob generate-sas --permissions r ...
// after: include list permission
az storage blob generate-sas --permissions rl ...
Defensive patterns

Strategy: try-catch

Validate before calling

// verify list permission before relying on directory artifacts
pager := containerClient.NewListBlobsFlatOptions(&azblob.ListBlobsFlatOptions{Prefix: &prefix})
if _, err := pager.NextPage(ctx); err != nil { return err }

Type guard

func isListAuthFailure(err error) bool {
	var respErr *azcore.ResponseError
	return errors.As(err, &respErr) && respErr.ErrorCode == "AuthorizationFailure"
}

Try / catch

err := driver.Load(ctx, artifact, path)
if strings.Contains(err.Error(), "unable to determine if") {
	var respErr *azcore.ResponseError
	if errors.As(err, &respErr) && respErr.ErrorCode == "AuthorizationFailure" {
		// grant List permission (Storage Blob Data Reader or SAS 'l')
	}
}

Prevention

When it happens

Trigger: DownloadFile returned BlobNotFound (or empty file) and then containerClient.NewListBlobsFlatPager/NextPage in IsDirectory fails: 403 AuthorizationFailure on list, ContainerNotFound, invalid SharedKey/SAS signature on list operations, transient network failure.

Common situations: SAS token lacking List permission (only Read), account key valid for get but firewall/rate limits hitting list calls, misconfigured endpoint so list goes to wrong account, HNS-enabled accounts with permission differences between read and list.

Related errors


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