ory/hydra · critical
key must be exactly %d bytes long, got %d bytes
Error message
key must be exactly %d bytes long, got %d bytes
What it means
The AEAD helper requires the encryption key to be exactly keySize (chacha20poly1305 key length, 32 bytes) long. When the configured AEAD key has any other length, encryptionKey/Encrypt returns this error because the cipher cannot be initialized with an invalid key.
Source
Thrown at aead/helpers.go:19
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package aead
import (
"context"
"fmt"
)
func encryptionKey(ctx context.Context, d Dependencies, keySize int) ([]byte, error) {
keys, err := allKeys(ctx, d)
if err != nil {
return nil, err
}
key := keys[0]
if len(key) != keySize {
return nil, fmt.Errorf("key must be exactly %d bytes long, got %d bytes", keySize, len(key))
}
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 {View on GitHub (pinned to 4174065ffb)
Solutions
- Set secrets.system (and any rotated keys) to a valid base64-encoded 32-byte (256-bit) value; generate with `openssl rand -base64 32`.
- Re-encode an existing key: if you have raw key bytes, base64-encode the full 32 bytes instead of a hex string or ASCII passphrase.
- Rotate the AEAD key properly via the aead.RotateKey API so old ciphertexts stay decryptable.
- Decrypt-and-reencrypt data with a correct key if old ciphertext was created under a wrong-length key.
Example fix
// before SECRETS_SYSTEM=super-secret-password // after SECRETS_SYSTEM=$(openssl rand -base64 32)
Defensive patterns
Strategy: validation
Validate before calling
// Validate key length before configuring/rotating:
key, err := base64.StdEncoding.DecodeString(cfgSecret)
if err != nil || len(key) != 32 {
panic(fmt.Sprintf("AEAD key must be 32 bytes, got %d", len(key)))
} Try / catch
// When calling Encrypt directly:
enc, err := x.Encrypt(ctx, plaintext)
if err != nil {
var keyErr interface{ error }
_ = keyErr
return fmt.Errorf("encrypt failed (check AEAD key is 32 base64 bytes): %w", err)
} Prevention
- Generate keys only with `openssl rand -base64 32`.
- Never use ASCII passwords or hex strings as secrets.system.
- Add a startup config check that decodes and length-checks the key.
- Keep current and rotated keys in a secrets manager, not hand-edited env files.
When it happens
Trigger: Calling Encrypt (via encryptionKey) when the first key in the AEAD key configuration (AEAD key material, e.g. secrets.system-derived) does not decode to exactly 32 bytes.
Common situations: Setting secrets.system to a too-short human-readable password instead of a 32-byte base64 value; rotating to a malformed key; pasting a key truncated or with wrong encoding (hex vs base64); upgrading Hydra where a shorter legacy key is still configured.
Related errors
- at least one encryption key must be defined but none were
- plaintext too large
- malformed ciphertext: too short
- cookiex: purpose must be non-empty and must not contain a pi
- cookiex: at least one secret is required
AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03).
Data as JSON: /api/errors/ade45f52ad4b8f71.
Report an issue: GitHub.