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 errView on GitHub (pinned to 35bff19146)
Solutions
- Inspect the wrapped cause (%w) to distinguish auth (401/403) vs network vs config errors.
- Verify cluster egress can reach <account>.blob.core.windows.net:443 (no firewall/proxy interference).
- Confirm the container exists and the credentials can list blobs: az storage blob list --container-name ...
- If transient, re-run the workflow deletion; the SDK has default retries but hard failures (auth) will not self-heal.
- 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
- Verify cluster egress to the storage endpoint from the executor network.
- Rotate SAS tokens before expiry and monitor for 403s.
- Pre-check artifact existence with ListObjects in your workflow tooling.
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
- unable to list files in %s: %w
- unable to download blob %s: %w
- unable to open stream for blob %s: %w
- unable to upload stream to Azure blob %s: %w
- error listing blobs %s in Azure Blob Storage container: %w
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/766a87512155a097.
Report an issue: GitHub.