ory/hydra · critical

at least one encryption key must be defined but none were

Error message

at least one encryption key must be defined but none were

What it means

allKeys collects the global (current) AEAD key plus rotated keys and refuses to proceed when none are configured. Decrypt and encryptionKey depend on this list, so decryption/encryption cannot run without at least one key.

Source

Thrown at aead/helpers.go:38

	}

	return key, nil
}

func allKeys(ctx context.Context, d Dependencies) ([][]byte, error) {
	global, err := d.GetGlobalSecret(ctx)
	if err != nil {
		return nil, err
	}

	rotated, err := d.GetRotatedGlobalSecrets(ctx)
	if err != nil {
		return nil, err
	}

	keys := append([][]byte{global}, rotated...)
	if len(keys) == 0 {
		return nil, fmt.Errorf("at least one encryption key must be defined but none were")
	}
	return keys, nil
}

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Set secrets.system / SECRETS_SYSTEM to a non-empty base64 32-byte key (openssl rand -base64 32).
  2. When moving deployments, keep the original key in rotated_keys so existing ciphertexts remain decryptable.
  3. Verify the environment variable actually reaches the process (docker-compose env, k8s secret mounted, config file loaded with -c).
  4. If data is unrecoverable, accept invalidation: reset the key and re-create affected secrets.

Example fix

// before (docker-compose)
environment: []

// after
environment:
  - SECRETS_SYSTEM=Q7N3...base64-32-bytes...
Defensive patterns

Strategy: validation

Validate before calling

// Before starting the service/janitor:
if os.Getenv("SECRETS_SYSTEM") == "" {
    panic("SECRETS_SYSTEM must be set (base64 32-byte key): openssl rand -base64 32")
}

Try / catch

// Wrap decryption so a missing-key config surfaces clearly:
val, err := x.Decrypt(ctx, ct)
if err != nil {
    return nil, fmt.Errorf("decrypt failed — is secrets.system configured and matching the key that encrypted this data? %w", err)
}

Prevention

When it happens

Trigger: Calling Decrypt or Encrypt when the AEAD dependency has an empty global key and no rotated keys (e.g. secrets.system unset or empty in configuration).

Common situations: Forgetting to set SECRETS_SYSTEM when deploying Hydra; empty string after config parsing; a fresh instance without the key used to encrypt previously stored data; environment variables not loaded in container orchestration.

Related errors


AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03). Data as JSON: /api/errors/b1b1e230e0f357b4. Report an issue: GitHub.