benbjohnson/litestream · error

abs: cannot list blobs: %w

Error message

abs: cannot list blobs: %w

What it means

This error is returned by ReplicaClient.DeleteAll when a page of the blob listing fails. DeleteAll pages through all blobs under the configured path prefix with a ListBlobsFlatPager and deletes each; if NextPage fails, listing (and thus the delete-all sweep) is aborted and the Azure error is wrapped with the key for diagnosis.

Source

Thrown at abs/replica_client.go:338

	}

	// List all blobs with the configured path prefix
	prefix := "/"
	if c.Path != "" {
		prefix = strings.TrimSuffix(c.Path, "/") + "/"
	}

	pager := c.client.NewListBlobsFlatPager(c.Bucket, &azblob.ListBlobsFlatOptions{
		Prefix:  &prefix,
		Include: azblob.ListBlobsInclude{Metadata: true},
	})

	for pager.More() {
		internal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, "LIST").Inc()

		resp, err := pager.NextPage(ctx)
		if err != nil {
			return fmt.Errorf("abs: cannot list blobs: %w", err)
		}

		for _, item := range resp.Segment.BlobItems {
			internal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, "DELETE").Inc()

			_, err := c.client.DeleteBlob(ctx, c.Bucket, *item.Name, nil)
			if isNotExists(err) {
				continue
			} else if err != nil {
				return fmt.Errorf("abs: cannot delete blob %q: %w", *item.Name, err)
			}
		}
	}

	return nil
}

type ltxFileIterator struct {

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Verify the container exists and the credentials have list permission on the account/container
  2. Check network connectivity and Azure status; retries are safe because DeleteAll is idempotent (missing blobs are skipped)
  3. Confirm the connection string / SAS token has not expired and the storage account endpoint is correct
  4. Re-run after fixing; there is no partial-state cleanup needed beyond re-invoking DeleteAll

Example fix

// before
err := client.DeleteAll(ctx) // fails listing with expired SAS
// after
if err := client.DeleteAll(ctx); err != nil {
	var respErr *azcore.ResponseError
	if errors.As(err, &respErr) && respErr.StatusCode == 403 {
		// refresh SAS token / connection string, then retry
	}
	return err
}
Defensive patterns

Strategy: retry

Validate before calling

// Go
if err := client.Init(ctx); err != nil { return err }
if _, err := client.client.NewListBlobsFlatPager(client.Bucket, &azblob.ListBlobsFlatOptions{Prefix: ptr("/")}).NextPage(ctx); err != nil {
	return fmt.Errorf("list permission check failed: %w", err)
}

Type guard

// Go
func isTransientListError(err error) bool {
	var re *azcore.ResponseError
	if errors.As(err, &re) {
		return re.StatusCode >= 500 || re.StatusCode == 429 || re.StatusCode == 408
	}
	return errors.Is(err, context.DeadlineExceeded) || errors.Is(err, io.EOF)
}

Try / catch

err := client.DeleteAll(ctx)
if err != nil && isTransientListError(err) {
	err = retryWithBackoff(func() error { return client.DeleteAll(ctx) })
}
return err

Prevention

When it happens

Trigger: Calling DeleteAll(ctx) while pager.NextPage(ctx) fails: invalid or expired credentials/SAS without list permission, storage account/container unreachable, firewall/DNS issues, or context cancellation mid-pagination.

Common situations: Wiping a replica with an account that can list but not enumerate (missing 'l' SAS permission); container deleted or renamed between listing pages; transient Azure outages; network blips during long delete sweeps.

Related errors


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/f2b532b25bd380b2. Report an issue: GitHub.