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

  1. Choose exactly one encryption method: keep either encryption_key (CSEK) or kms_encryption_key (CMEK), and remove the other.
  2. If using env vars, unset the one you do not want (e.g. 'unset GOOGLE_ENCRYPTION_KEY').
  3. 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

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


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