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
- Choose ONE encryption strategy: keep either `encryption_key` (customer-supplied) or `kms_encryption_key` (KMS-managed), not both.
- Remove the unused attribute from the backend block entirely so it is null in cty (not empty string).
- Unset the corresponding environment variable (GOOGLE_ENCRYPTION_KEY or GOOGLE_KMS_ENCRYPTION_KEY) if it is no longer intended.
- 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
- Set exactly one of `encryption_key` or `kms_encryption_key` in the backend block.
- When migrating from customer keys to KMS, delete the old attribute (do not leave it empty).
- Unset the matching env var (GOOGLE_ENCRYPTION_KEY / GOOGLE_KMS_ENCRYPTION_KEY) when switching strategies.
- Run `terraform init` after backend edits to validate early.
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
- bucket not exists
- Cannot set both 'source' and 'content'
- error marshaling config
- failed to create bucket
- failed to create tag
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)