hashicorp/terraform · error

querying Cloud Storage failed: %v

Error message

querying Cloud Storage failed: %v

What it means

Workspaces() lists state files in the bucket by calling bucket.Objects(...).Next() in a loop; any non-iterator.Done error from the GCS list API is surfaced here. This is the first network/object call after NewClient, so it commonly reflects bucket-existence or IAM issues rather than auth (auth would already have failed in NewClient).

Source

Thrown at internal/backend/remote-state/gcs/backend_state.go:47

// state is always returned as the first element in the slice.
func (b *Backend) Workspaces() ([]string, tfdiags.Diagnostics) {
	var diags tfdiags.Diagnostics
	ctx := context.TODO()

	states := []string{backend.DefaultStateName}

	bucket := b.storageClient.Bucket(b.bucketName)
	objs := bucket.Objects(ctx, &storage.Query{
		Delimiter: "/",
		Prefix:    b.prefix,
	})
	for {
		attrs, err := objs.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return nil, diags.Append(fmt.Errorf("querying Cloud Storage failed: %v", err))
		}

		name := path.Base(attrs.Name)
		if !strings.HasSuffix(name, stateFileSuffix) {
			continue
		}
		st := strings.TrimSuffix(name, stateFileSuffix)

		if st != backend.DefaultStateName {
			states = append(states, st)
		}
	}

	sort.Strings(states[1:])
	return states, diags
}

// DeleteWorkspace deletes the named workspaces. The "default" state cannot be deleted.

View on GitHub (pinned to c9def3e214)

Solutions

  1. Confirm the bucket exists: 'gsutil ls gs://<bucket>' and matches backend.bucket.
  2. Grant the SA 'roles/storage.objectAdmin' (read+list+write) or at minimum 'roles/storage.objectViewer' plus objectViewer on the prefix.
  3. If the %v says 'notFound', fix the bucket name; if 'forbidden', fix IAM; if transient, retry 'terraform workspace list'.
  4. Confirm prefix doesn't escape into a bucket the SA lacks access to.

Example fix

// before: bucket typo
bucket = "myco-terraform-state-prod"

// after
bucket = "myco-tfstate-prod"  # actual bucket
gsutil iam ch serviceAccount:tf-deployer@proj.iam.gserviceaccount.com:roles/storage.objectAdmin gs://myco-tfstate-prod
Defensive patterns

Strategy: retry

Validate before calling

// Pre-flight: confirm bucket is listable
import "cloud.google.com/go/storage"
func bucketListable(ctx context.Context, c *storage.Client, bucket string) error {
    it := c.Bucket(bucket).Objects(ctx, nil)
    if _, err := it.Next(); err != nil && err != iterator.Done { return err }
    return nil
}

Try / catch

var states []string
for attempt := 0; attempt < 3; attempt++ {
    var diags tfdiags.Diagnostics
    states, diags = backend.Workspaces()
    if !diags.HasErrors() { break }
    if !isTransientGCS(diags) { break }
    time.Sleep(backoff(attempt))
}

Prevention

When it happens

Trigger: 'terraform workspace list' (or any command that enumerates workspaces) against a bucket the caller can't storage.objects.list, a bucket name that doesn't exist, an invalid prefix, or transient GCS API errors.

Common situations: Wrong bucket name in backend config; new SA without 'roles/storage.objectViewer' / 'roles/storage.objectAdmin'; cross-project bucket without granted access; transient 5xx from GCS.

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/05eb8275bcf50e37. Report an issue: GitHub.