hashicorp/terraform · error

listing blobs

Error message

listing blobs: %v

What it means

Thrown by Workspaces() when client.ListBlobs(ctx, containerName, params) fails after the container client was successfully built. ListBlobs calls the Azure Blob REST List operation against the container to enumerate state files under the env: prefix. Fails on auth, container-not-found, network/firewall, or RBAC.

Solutions

  1. Verify container_name with `az storage container list --account-name <acct>`.
  2. With use_azuread_auth, grant Storage Blob Data Reader (list) / Data Contributor (read-write) to the identity.
  3. Check storage account Networking: allow your client IP or set AzureServices bypass.
  4. Reproduce with `az storage blob list -c <container> --account-name <acct> --auth-mode login` to isolate Terraform vs Azure.
  5. Confirm the container was not deleted; recreate if needed.
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight: confirm the container exists and the identity can list blobs.
func canListBlobs(ctx context.Context, acct, container string) error {
    cmd := exec.CommandContext(ctx, "az", "storage", "blob", "list",
        "--account-name", acct, "-c", container, "--auth-mode", "login", "--query", "[0].name", "-o", "tsv")
    out, err := cmd.CombinedOutput()
    if err != nil { return fmt.Errorf("cannot list blobs in %s/%s: %w (%s)", acct, container, err, out) }
    return nil
}

Prevention

When it happens

Trigger: (a) container_name does not exist in the storage account. (b) Identity can authenticate but cannot list blobs (data-plane RBAC missing). (c) Storage account firewall / private endpoint blocks the request. (d) Container was deleted.

Common situations: Typo in container_name; storage account network firewall set to deny without your IP allowed; principal has Storage Account Contributor (control plane) but not Storage Blob Data Reader (data plane) when using AAD; container deleted out-of-band.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/777c783df43bcba2. Report an issue: GitHub.

Appendix: source

Thrown at internal/backend/remote-state/azure/backend_state.go:41

	// reduce the chance of name conflicts with existing objects.
	keyEnvPrefix = "env:"
)

func (b *Backend) Workspaces() ([]string, tfdiags.Diagnostics) {
	var diags tfdiags.Diagnostics
	prefix := b.keyName + keyEnvPrefix
	params := containers.ListBlobsInput{
		Prefix: &prefix,
	}

	ctx := newCtx()
	client, err := b.apiClient.getContainersClient(ctx)
	if err != nil {
		return nil, diags.Append(fmt.Errorf("retrieving container client: %v", err))
	}
	resp, err := client.ListBlobs(ctx, b.containerName, params)
	if err != nil {
		return nil, diags.Append(fmt.Errorf("listing blobs: %v", err))
	}

	envs := map[string]struct{}{}
	for _, obj := range resp.Blobs.Blobs {
		key := obj.Name
		if strings.HasPrefix(key, prefix) {
			name := strings.TrimPrefix(key, prefix)
			// we store the state in a key, not a directory
			if strings.Contains(name, "/") {
				continue
			}

			envs[name] = struct{}{}
		}
	}

	result := []string{backend.DefaultStateName}
	for name := range envs {

View on GitHub (pinned to d32a084675)