hashicorp/terraform · error

S3 bucket does not exist. The referenced S3 bucket must…

Error message

S3 bucket %q does not exist.

The referenced S3 bucket must have been previously created. If the S3 bucket
was created within the last minute, please wait for a minute or two and try
again.

Error: %s

What it means

Thrown by Backend.Workspaces in the S3 state backend when ListObjectsV2 paginator's NextPage returns an error that IsA[*s3types.NoSuchBucket] matches. The errS3NoSuchBucket template explains the bucket must have been pre-created and that very-recent creation may need a minute to propagate. This is a config/existence failure, not transient.

Solutions

  1. Verify the bucket exists: `aws s3api head-bucket --bucket <name>` with the same credentials/profile OpenTofu uses.
  2. Check the `region` in the backend block matches the bucket's region.
  3. Confirm the AWS profile/role assumed by OpenTofu is the one that owns the bucket.
  4. If the bucket was just created, wait 60-120s for global consistency and re-run `tofu init`.

Example fix

# before
terraform {
  backend "s3" {
    bucket = "my-state-bukcet"  # typo
  }
}
# after
terraform {
  backend "s3" {
    bucket = "my-state-bucket"
  }
}
Defensive patterns

Strategy: validation

Validate before calling

// preflight: head the bucket with the exact credentials tofu will use
ctx := context.Background()
_, err := s3Client.HeadBucket(ctx, &s3.HeadBucketInput{Bucket: aws.String(bucketName)})
if err != nil { return fmt.Errorf("bucket %s not visible: %w", bucketName, err) }

Type guard

func isNoSuchBucketErr(err error) bool {
    var nsb *s3types.NoSuchBucket
    return errors.As(err, &nsb) || strings.Contains(err.Error(), "NoSuchBucket")
}

Prevention

When it happens

Trigger: s3.NewListObjectsV2Paginator(...).NextPage(ctx) errors and the error type-asserts to *s3types.NoSuchBucket. The bucket b.bucketName does not exist in the configured account/region, or the credentials point at the wrong account.

Common situations: Typo in `bucket` in the S3 backend block; bucket in a different region than configured; bucket in a different AWS account and the principal has no cross-account access; bucket literally not yet created; wrong profile/role assumed so the caller sees a different account.

Related errors


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

Appendix: source

Thrown at internal/backend/remote-state/s3/backend_state.go:72

	)

	params := &s3.ListObjectsV2Input{
		Bucket:  aws.String(b.bucketName),
		Prefix:  aws.String(prefix),
		MaxKeys: aws.Int32(maxKeys),
	}

	wss := []string{backend.DefaultStateName}

	ctx, baselog := baselogging.NewHcLogger(ctx, log)
	ctx = baselogging.RegisterLogger(ctx, baselog)

	pages := s3.NewListObjectsV2Paginator(b.s3Client, params)
	for pages.HasMorePages() {
		page, err := pages.NextPage(ctx)
		if err != nil {
			if IsA[*s3types.NoSuchBucket](err) {
				return nil, diags.Append(fmt.Errorf(errS3NoSuchBucket, b.bucketName, err))
			}
			if foo, ok := As[smithy.APIError](err); b.workspaceKeyPrefix == defaultWorkspaceKeyPrefix && ok && foo.ErrorCode() == "AccessDenied" {
				log.Warn("Unable to list non-default workspaces", "err", err.Error())
				return wss[:1], nil
			}
			return nil, diags.Append(fmt.Errorf("Unable to list objects in S3 bucket %q with prefix %q: %w", b.bucketName, prefix, err))
		}

		for _, obj := range page.Contents {
			ws := b.keyEnv(aws.ToString(obj.Key))
			if ws != "" {
				wss = append(wss, ws)
			}
		}
	}

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

View on GitHub (pinned to d32a084675)