TL;DR: Inside `encryption {}` key providers, the passphrase must be a BARE variable reference (`var.state_passphrase`), not an expression, function call, or conditional. Anything fancier fails with `Invalid expression`. Keep the secret out of shell history by exporting it as an env var the variable reads.

```text
Error: Invalid expression

A single static variable reference is required
```

## Steps

1. Find the offending argument in the `encryption {}` block (usually `key_provider "pbkdf2"` passphrase).
   Expected: something like `passphrase = var.prefix + "-suffix"` or `passphrase = coalesce(...)`.
2. Replace it with a single static variable reference:
   ```hcl
   encryption {
     key_provider "pbkdf2" "passphrase" {
       passphrase = var.state_passphrase
     }
     ...
   }
   ```
   Expected: the argument is exactly `var.[name]`.
3. Supply the value out of band: `export TF_VAR_state_passphrase='[your passphrase]'` (or a `.tfvars` file that is gitignored).
   Expected: no secret in the config or shell history.
4. Re-run.
   Expected: the `Invalid expression` error is gone.

## When this applies

- `Error: Invalid expression` / `A single static variable reference is required` pointing at an `encryption {}` block.
- The passphrase argument contains concatenation, conditionals, or functions.

## When it doesn't apply

- `Missing required argument` on the key_provider: the argument is absent entirely, different fix.
- Wrong passphrase at runtime: that's a decryption failure, not an expression error.

## Tool versions

OpenTofu 1.7+ (native state encryption).

## Why it happens

The encryption configuration is evaluated before variables are fully resolved, in a restricted context where only static references are allowed. Expressions would need the full evaluation machinery that isn't available that early, so tofu rejects them outright instead of half-evaluating.

## Edge cases

- PBKDF2 passphrases must be at least 16 characters; a shorter one fails at the key provider with a different error. Generate a long one.
- `enforced = true` refuses to write unencrypted state; enable it only once every collaborator and CI job has the passphrase, or you'll lock the team out.
- Anything reading state WITHOUT the encryption config (CI linters, `tofu init -backend=false` gates, jq one-liners) sees an opaque envelope, not an error naming encryption; teach the team that envelope means "missing encryption config".