ory/hydra · error · ErrInvalidCookie
cookiex: cookie could not be decoded
Error message
cookiex: cookie could not be decoded
What it means
cookiex's ErrInvalidCookie is returned when a cookie with the requested name is present but none of its values could be decoded or matched a known key — base64 decoding fails, no secret matches the MAC, the plaintext JSON is not the expected envelope, or it failed other decode steps. It wraps the raw decode errors with errors.WithStack, so callers should use errors.As to identify it.
Source
Thrown at oryx/cookiex/cookiex.go:46
// formatPrefix marks the versioned wire format. A dot is not part of the
// base64url alphabet, so legacy securecookie values can never collide
// with the prefix.
formatPrefix = "v1."
// kdfInfo domain-separates the HKDF key derivation.
kdfInfo = "ory/x/cookiex/v1"
// aadPrefix domain-separates the additional authenticated data.
aadPrefix = "ory/x/cookiex/v1"
// defaultMaxAge matches the gorilla/securecookie default that the
// previous cookie stores relied on.
defaultMaxAge = 30 * 24 * time.Hour
// maxCookieValueLength is the browser limit that securecookie also
// enforced.
maxCookieValueLength = 4096
)
// ErrInvalidCookie is returned when a cookie with the requested name is
// present but none of its values could be decoded (or none matched).
var ErrInvalidCookie = errors.New("cookiex: cookie could not be decoded")
type (
// Option configures a Codec.
Option func(*config)
config struct {
maxAge time.Duration
legacyKeyPairs [][]byte
legacyEncode bool
}
)
// WithMaxAge overrides how old a cookie may be before decoding rejects it.
// The default is 30 days; zero disables the check. The value must not be
// negative.
func WithMaxAge(d time.Duration) Option {
return func(c *config) { c.maxAge = d }
}View on GitHub (pinned to 4174065ffb)
Solutions
- Keep old secrets in the `secrets` slice (prepend new ones) so previously sealed cookies still verify.
- Have the caller treat ErrInvalidCookie as 'anonymous/no session' (log and continue) rather than a hard failure — clear the bad cookie.
- Ensure the same `purpose` string is used for sealing and opening.
- Check middleware/proxies for cookie truncation or re-encoding, and keep values under 4096 bytes.
Example fix
// before
val, err := codec.GetMatching(r, "session")
if err != nil { http.Error(w, "server error", 500) }
// after
val, err := codec.GetMatching(r, "session")
if errors.Is(err, cookiex.ErrInvalidCookie) {
http.SetCookie(w, &http.Cookie{Name: "session", MaxAge: -1}) // clear bad cookie
val = zeroValue // treat as unauthenticated
} Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check the raw cookie before decoding
func cookieLooksPlausible(c *http.Cookie) bool {
return c != nil && c.Value != "" && len(c.Value) <= 4096
} Type guard
func isErrInvalidCookie(err error) bool {
return errors.Is(err, cookiex.ErrInvalidCookie)
} Try / catch
val, err := codec.GetMatching(r, "session")
switch {
case errors.Is(err, cookiex.ErrInvalidCookie):
log.Warn("undecodable cookie; clearing and continuing as anonymous")
http.SetCookie(w, &http.Cookie{Name: "session", MaxAge: -1})
val = cookiexZeroValue
case err != nil:
http.Error(w, "internal error", http.StatusInternalServerError)
return
} Prevention
- Keep old secrets in the secrets list when rotating (prepend new)
- Use the same purpose string for sealing and opening
- Watch for proxies/load balancers truncating large cookies
- On format migrations, keep the legacy open path available during rollout
When it happens
Trigger: Codec.GetMatching/open returns ErrInvalidCookie when base64.RawURLEncoding decoding of the cookie value fails, or during open when no configured key verifies the value, or json.Unmarshal of the decrypted plaintext into the envelope fails (cookiex.go:158/174).
Common situations: Cookie written by an older/other app version with a different format or purpose string; secrets rotated so the old signing key is no longer in the key list; cookie truncated by proxies or the 4096-byte browser limit; cookie manually edited or corrupted; switching cookie codec (e.g. from legacy openLegacy format) without migration.
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: legacy encode requires legacy key pairs
- cookiex: payload must be a flat JSON object with string valu
AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03).
Data as JSON: /api/errors/e8250579603e26b8.
Report an issue: GitHub.