argoproj/argo-workflows · error

unable to delete Azure Blob %s: %w

Error message

unable to delete Azure Blob %s: %w

What it means

DeleteBlob performs blobClient.Delete and returns this error for any failure that is not BlobNotFound (BlobNotFound is swallowed when allowNonExistent=true). Typical causes are HTTP 403 (auth/permissions), 409, lease conflicts, or network errors.

Source

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

		}
	}
	if directoryFile != "" {
		return DeleteBlob(ctx, containerClient, directoryFile, true)
	}
	return nil
}

func DeleteBlob(ctx context.Context, containerClient *container.Client, blobName string, allowNonExistent bool) error {
	blobClient := containerClient.NewBlobClient(blobName)

	_, err := blobClient.Delete(ctx, nil)
	if err != nil {
		if allowNonExistent && bloberror.HasCode(err, bloberror.BlobNotFound) {
			logger := logging.RequireLoggerFromContext(ctx)
			logger.WithField("blob", blobName).WithError(err).Debug(ctx, "blob to delete does not exist")
			return nil
		}
		return fmt.Errorf("unable to delete Azure Blob %s: %w", blobName, err)
	}
	return nil
}

// ListObjects lists the files in Azure Blob Storage
func (azblobDriver *ArtifactDriver) ListObjects(ctx context.Context, artifact *wfv1.Artifact) ([]string, error) {
	var files []string
	logger := logging.RequireLoggerFromContext(ctx)
	logger.WithField("endpoint", artifact.Azure.Endpoint).
		WithField("container", artifact.Azure.Container).
		WithField("blob", artifact.Azure.Blob).
		Info(ctx, "Listing blobs in Azure Blob Storage")

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

View on GitHub (pinned to 35bff19146)

Solutions

  1. Check the wrapped azblob error code: AuthorizationFailure means grant 'Storage Blob Data Contributor' to the identity.
  2. If the blob is leased or immutable, break the lease / remove immutability policy before deleting.
  3. Confirm the SAS token has delete (w/d) permission and has not expired.
  4. If the blob legitimately may not exist, call DeleteBlob with allowNonExistent=true (Delete already does this) so BlobNotFound is ignored.
  5. Retry on transient network errors; the SDK's default retry policy covers some of this.

Example fix

// before: hard failure on missing blob
err := DeleteBlob(ctx, client, name, false)
// after: tolerate missing blob
err := DeleteBlob(ctx, client, name, true)
Defensive patterns

Strategy: type-guard

Validate before calling

// check delete permission ahead of time via a harmless probe
err := driver.Delete(ctx, testArtifact)
if err != nil && bloberror.HasCode(err, bloberror.AuthorizationFailure) {
    return errors.New("credential lacks delete permission on container")
}

Type guard

var respErr *azcore.ResponseError
if errors.As(err, &respErr) && bloberror.HasCode(err, bloberror.BlobNotFound) {
    // treat as already-deleted success
}

Try / catch

if err := driver.Delete(ctx, artifact); err != nil {
    var respErr *azcore.ResponseError
    if errors.As(err, &respErr) && respErr.ErrorCode == string(bloberror.BlobNotFound) {
        return nil // idempotent delete
    }
    return err
}

Prevention

When it happens

Trigger: Deleting a blob that is leased/immutable (immutability policy or legal hold); credentials lacking delete permission (no Storage Blob Data Contributor role); an active snapshot/writes-with-lease; transient network failure; delete called with allowNonExistent=false on a missing blob.

Common situations: Soft-delete/immutable storage policies on the account; SAS token scoped read-only; RBAC role missing delete action; blob name casing mismatch with allowNonExistent=false.

Related errors


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