hashicorp/terraform · error

can't set both encryption_key and kms_encryption_key

Error message

can't set both encryption_key and kms_encryption_key

What it means

Returned by the GCS remote-state backend's Configure() when both `encryption_key` and `kms_encryption_key` are configured. These are mutually exclusive ways to encrypt state in GCS — a customer-supplied key vs a Cloud KMS key — and Terraform cannot decide which to apply. The check fires on the non-empty string values and is then re-enforced on the raw cty attributes to also reject both-empty-but-present configs.

Solutions

  1. Choose ONE encryption strategy: keep either `encryption_key` (customer-supplied) or `kms_encryption_key` (KMS-managed), not both.
  2. Remove the unused attribute from the backend block entirely so it is null in cty (not empty string).
  3. Unset the corresponding environment variable (GOOGLE_ENCRYPTION_KEY or GOOGLE_KMS_ENCRYPTION_KEY) if it is no longer intended.
  4. Re-run `terraform init` after editing the backend block so Configure re-evaluates.

Example fix

// before: both keys configured
backend "gcs" {
  bucket="tf-state"
  encryption_key="base64-key"
  kms_encryption_key="projects/p/locations/global/keyRings/kr/cryptoKeys/k"
}
// after: keep only one
backend "gcs" {
  bucket="tf-state"
  kms_encryption_key="projects/p/locations/global/keyRings/kr/cryptoKeys/k"
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate backend config before `terraform init` so Configure() never sees both keys
func validateGCSBackend(cfg map[string]any) error {
    enc, hasEnc := cfg["encryption_key"]
    kms, hasKms := cfg["kms_encryption_key"]
    encSet := hasEnc && enc != nil && fmt.Sprint(enc) != ""
    kmsSet := hasKms && kms != nil && fmt.Sprint(kms) != ""
    if encSet && kmsSet {
        return fmt.Errorf("set only one of encryption_key or kms_encryption_key")
    }
    // also enforce the 'both present even if empty' rule
    if hasEnc && hasKms {
        return fmt.Errorf("remove one of encryption_key/kms_encryption_key from the config")
    }
    return nil
}

Type guard

// Type guard over the parsed cty value: exactly one of the two is non-null
func exactlyOneEncryptionKey(v cty.Value) bool {
    encNull := v.GetAttr("encryption_key").IsNull()
    kmsNull := v.GetAttr("kms_encryption_key").IsNull()
    return encNull != kmsNull // XOR
}

Try / catch

// Not applicable — this is a config-time validation error, not a runtime
// exception to catch. Fix the backend block and re-run terraform init.

Prevention

When it happens

Trigger: Backend `cloud`/`gcs` block sets both `encryption_key` (or GOOGLE_ENCRYPTION_KEY env) and `kms_encryption_key` (or GOOGLE_KMS_ENCRYPTION_KEY env) to non-empty values, OR both attributes appear in the config block (even as empty strings) without one being null.

Common situations: Copy-pasting a backend block that grew over time and now has both keys; setting GOOGLE_ENCRYPTION_KEY in the shell while the config also declares kms_encryption_key; migrating from customer-managed keys to KMS and forgetting to remove the old attribute; both set to empty string in HCL which the second check still rejects.

Related errors


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

Appendix: source

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

			},
		},
	}
}

func (b *Backend) Configure(configVal cty.Value) tfdiags.Diagnostics {
	if b.storageClient != nil {
		return nil
	}

	// TODO: Update the Backend API to pass the real context.Context from
	// the running command.
	ctx := context.TODO()

	data := backendbase.NewSDKLikeData(configVal)

	if data.String("encryption_key") != "" && data.String("kms_encryption_key") != "" {
		return backendbase.ErrorAsDiagnostics(
			fmt.Errorf("can't set both encryption_key and kms_encryption_key"),
		)
	}
	// The above catches the main case where both of the arguments are set to
	// a non-empty value, but we also want to reject the situation where
	// both are present in the configuration regardless of what values were
	// assigned to them. (This check doesn't take the environment variables
	// into account, so must allow neither to be set in the main configuration.)
	if !(configVal.GetAttr("encryption_key").IsNull() || configVal.GetAttr("kms_encryption_key").IsNull()) {
		// This rejects a configuration like:
		//     encryption_key     = ""
		//     kms_encryption_key = ""
		return backendbase.ErrorAsDiagnostics(
			fmt.Errorf("can't set both encryption_key and kms_encryption_key"),
		)
	}

	b.bucketName = data.String("bucket")
	b.prefix = strings.TrimLeft(data.String("prefix"), "/")

View on GitHub (pinned to d32a084675)