ory/hydra · error
cookiex: legacy encode requires legacy key pairs
Error message
cookiex: legacy encode requires legacy key pairs
What it means
cookiex.New requires that enabling legacy encoding (WithLegacyEncode) is accompanied by at least one legacy key pair (WithLegacyKeyPairs), because the legacy securecookie codec cannot sign/encrypt without keys. New returns this error when legacyEncode is true but legacyKeyPairs is empty.
Source
Thrown at oryx/cookiex/cookiex.go:98
// character. The codec seals with a key derived from the first secret
// and opens with keys derived from any of them, so secrets rotate by
// prepending a new one.
func New[T any](purpose string, secrets [][]byte, opts ...Option) (*Codec[T], error) {
if purpose == "" || strings.Contains(purpose, "|") {
return nil, errors.New("cookiex: purpose must be non-empty and must not contain a pipe character")
}
if len(secrets) == 0 {
return nil, errors.New("cookiex: at least one secret is required")
}
cfg := config{maxAge: defaultMaxAge}
for _, opt := range opts {
opt(&cfg)
}
if cfg.maxAge < 0 {
return nil, errors.New("cookiex: max age must not be negative")
}
if cfg.legacyEncode && len(cfg.legacyKeyPairs) == 0 {
return nil, errors.New("cookiex: legacy encode requires legacy key pairs")
}
keys := make([][32]byte, len(secrets))
for i, secret := range secrets {
key, err := hkdf.Key(sha256.New, secret, nil, kdfInfo, 32)
if err != nil {
return nil, errors.Wrap(err, "cookiex: cannot derive key")
}
keys[i] = [32]byte(key)
}
return &Codec[T]{
purpose: purpose,
keys: keys,
maxAge: cfg.maxAge,
legacy: newLegacyState(cfg, cfg.maxAge),
now: time.Now,
}, nil
}
View on GitHub (pinned to 4174065ffb)
Solutions
- Pass at least one key pair via WithLegacyKeyPairs, e.g. cookiex.WithLegacyKeyPairs([][]byte{oldHashKey, oldBlockKey}).
- If legacy encoding is not needed, remove the WithLegacyEncode option.
- Load the legacy key pairs from config at startup and log/fail if they are empty before calling New.
Example fix
// before
cc, err := cookiex.New([][]byte{secret}, cookiex.WithLegacyEncode(true))
// after
cc, err := cookiex.New([][]byte{secret},
cookiex.WithLegacyEncode(true),
cookiex.WithLegacyKeyPairs([][]byte{hashKey, blockKey})) Defensive patterns
Strategy: validation
Validate before calling
if len(legacyKeyPairs) == 0 {
return errors.New("legacy encode requested but no legacy key pairs configured")
}
cc, err := cookiex.New(secrets,
cookiex.WithLegacyEncode(true),
cookiex.WithLegacyKeyPairs(legacyKeyPairs)) Try / catch
cfg, err := cookiex.New(secrets, opts...)
if err != nil {
log.Fatalf("cookiex configuration invalid: %v", err)
} Prevention
- Pair WithLegacyEncode with WithLegacyKeyPairs in a single helper so they cannot drift
- Check env/config for legacy keys at boot when migration mode is on
- Disable legacy encode once migration completes to remove the requirement
When it happens
Trigger: Calling cookiex.New(secrets...) with cookiex.WithLegacyEncode(true) (or equivalent) and no WithLegacyKeyPairs(...) option, so cfg.legacyKeyPairs has length 0.
Common situations: Migrating from the old securecookie-based format and enabling legacy encoding without copying over the previous key pairs; loading key pairs from env/config that came back empty; conditional wiring where the legacy option is set but the key-pair option is skipped.
Understand the failure class
Background: "X is required", "must be set", "cannot be empty": the missing-required-config error family, from Vertex AI project/location to WeChat keys — this error's family across 18 libraries.
Related errors
- cookiex: purpose must be non-empty and must not contain a pi
- cookiex: at least one secret is required
- cookiex: max age must not be negative
- cookiex: payload must be a flat JSON object with string valu
- key not found
AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03).
Data as JSON: /api/errors/8c3d1445d0b54b17.
Report an issue: GitHub.