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

  1. Set secrets.system (and any rotated keys) to a valid base64-encoded 32-byte (256-bit) value; generate with `openssl rand -base64 32`.
  2. 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.
  3. Rotate the AEAD key properly via the aead.RotateKey API so old ciphertexts stay decryptable.
  4. 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

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


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