hashicorp/terraform · error

querying Cloud Storage failed

Error message

querying Cloud Storage failed: %v

What it means

Thrown while listing state objects in the bucket during Workspaces. The backend paginates bucket.Objects with a Delimiter+Prefix query; any non-iterator.Done error from objs.Next is wrapped here.

Solutions

  1. Grant the service account storage.objects.list (and get) on the bucket, e.g. roles/storage.objectAdmin or roles/storage.objectViewer.
  2. Confirm the bucket name in the backend block matches an existing bucket.
  3. Inspect the wrapped %v error to distinguish permission vs. existence vs. quota.
  4. Retry transient failures after verifying IAM propagation.

Example fix

// before — service account lacks list permission
// after
gsutil iam ch serviceAccount:terraform@proj.iam.gserviceaccount.com:roles/storage.objectViewer gs://tf-state
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight: verify bucket is listable before running terraform.
// gsutil ls gs://<bucket>/<prefix> should return successfully.

Try / catch

// Distinguish permission errors from transient ones.
for {
    attrs, err := objs.Next()
    if err == iterator.Done { break }
    if err != nil {
        if isTransient(err) { continue } // or backoff
        return fmt.Errorf("querying Cloud Storage failed: %w", err)
    }
    _ = attrs
}

Prevention

When it happens

Trigger: bucket.Objects iteration returns a non-Done error — insufficient IAM permission (storage.objects.list) on the bucket, bucket does not exist, network/transport error, or request quota exceeded.

Common situations: Service account has storage.objectAdmin but is missing the list permission for the chosen prefix; the configured bucket was renamed or deleted; transient 429/5xx from GCS; wrong project credential scoped to a different bucket.

Related errors


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

Appendix: 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 d32a084675)