argoproj/argo-workflows · error

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

Error message

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

What it means

Delete first calls IsDirectory to decide whether the artifact is a single blob or a directory prefix. If the IsDirectory probe fails (client creation, network error, or Azure list error), Delete wraps it as 'unable to test if %s is a directory'. The wrapped error is usually 430/437 underneath.

Source

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

	}
	return nil
}

// Delete deletes an artifact from a Azure Blob Storage
func (azblobDriver *ArtifactDriver) Delete(ctx context.Context, artifact *wfv1.Artifact) error {
	logger := logging.RequireLoggerFromContext(ctx)
	logger.WithField("endpoint", artifact.Azure.Endpoint).
		WithField("container", artifact.Azure.Container).
		WithField("blob", artifact.Azure.Blob).
		Info(ctx, "Deleting object from Azure Blob Storage")
	containerClient, err := azblobDriver.newAzureContainerClient(ctx)
	if err != nil {
		return fmt.Errorf("unable to create Azure Blob Container client: %w", err)
	}

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

	if !isDir {
		return DeleteBlob(ctx, containerClient, artifact.Azure.Blob, true)
	}
	files, err := azblobDriver.ListObjects(ctx, artifact)
	if err != nil {
		return fmt.Errorf("unable to list files in %s: %w", artifact.Azure.Blob, err)
	}
	directoryFile := ""
	for _, file := range files {
		if file == artifact.Azure.Blob {
			directoryFile = file
			continue
		}

		if err := DeleteBlob(ctx, containerClient, file, true); err != nil {
			return err

View on GitHub (pinned to 35bff19146)

Solutions

  1. Inspect the wrapped cause (%w) to distinguish auth (401/403) vs network vs config errors.
  2. Verify cluster egress can reach <account>.blob.core.windows.net:443 (no firewall/proxy interference).
  3. Confirm the container exists and the credentials can list blobs: az storage blob list --container-name ...
  4. If transient, re-run the workflow deletion; the SDK has default retries but hard failures (auth) will not self-heal.
  5. Fix the underlying client creation problem first (see 'unable to create Azure Blob Container client' guidance).
Defensive patterns

Strategy: retry

Validate before calling

// probe reachability before the API call
_, err := driver.ListObjects(ctx, artifact)
if err != nil {
    return fmt.Errorf("azure storage unreachable for artifact %s: %w", artifact.Azure.Blob, err)
}

Try / catch

err := driver.Delete(ctx, artifact)
if err != nil && strings.Contains(err.Error(), "unable to test if") {
    if retryable(err) { // network/5xx, not 401/403
        time.Sleep(backoff)
        err = driver.Delete(ctx, artifact)
    }
}

Prevention

When it happens

Trigger: Calling Delete on an artifact when the storage account/container is unreachable (DNS, firewall, private endpoint), credentials are rejected (401/403), or the container client cannot be built — any failure inside the pager.NextPage list call in IsDirectory.

Common situations: Storage account behind a firewall that blocks the cluster egress; deleted or renamed storage account; expired SAS token; transient network blips during artifact deletion; container name mismatch between config and actual storage account.

Related errors


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