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
Raised by the GCS backend's Configure() when both 'encryption_key' and 'kms_encryption_key' resolve to non-empty values. GCS state can be encrypted with either a customer-supplied key (CSEK) or a customer-managed KMS key (CMEK), but not both at once — specifying both is ambiguous and rejected. This is the value-based check (the second check at line 156 handles the both-present-but-empty case).
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 c9def3e214)
Solutions
- Choose exactly one encryption method: keep either encryption_key (CSEK) or kms_encryption_key (CMEK), and remove the other.
- If using env vars, unset the one you do not want (e.g. 'unset GOOGLE_ENCRYPTION_KEY').
- Re-run 'terraform init' after editing the backend block.
Example fix
# before: both set
terraform {
backend "gcs" {
bucket = "tf-state"
encryption_key = "Qk...=="
kms_encryption_key = "projects/p/locations/global/keyRings/r/cryptoKeys/k"
}
}
# after: keep only one (CMEK here)
terraform {
backend "gcs" {
bucket = "tf-state"
kms_encryption_key = "projects/p/locations/global/keyRings/r/cryptoKeys/k"
}
} Defensive patterns
Strategy: validation
Validate before calling
// Validate the backend config before init.
func validateGCSBackend(encKey, kmsKey string) error {
if encKey != "" && kmsKey != "" {
return fmt.Errorf("can't set both encryption_key and kms_encryption_key")
}
return nil
} Prevention
- Set exactly one of encryption_key / kms_encryption_key (or neither).
- Unset GOOGLE_ENCRYPTION_KEY / GOOGLE_KMS_ENCRYPTION_KEY when not in use.
- Lint the backend block in CI to catch dual-set configs.
When it happens
Trigger: At backend.go:146-149: data.String("encryption_key") != "" && data.String("kms_encryption_key") != "". Triggered on 'terraform init' when the backend block (or GOOGLE_ENCRYPTION_KEY + GOOGLE_KMS_ENCRYPTION_KEY env vars) supplies both keys.
Common situations: Configuring both encryption methods intentionally by mistake; GOOGLE_ENCRYPTION_KEY left exported in the shell while also setting kms_encryption_key in the backend block; copy-pasting a backend block from docs and forgetting to delete one.
Related errors
- the string provided in credentials is neither valid json nor
- Error loading credentials: %s
- Error loading encryption key: %s
- Error decoding encryption key: %s
- Failed to open state file at %v: %v
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/4e276c1c4cd2a5b1.
Report an issue: GitHub.