ory/hydra · warning
ErrMismatchedHashAndPassword
ErrMismatchedHashAndPassword
Error message
passwords do not match
What it means
ErrMismatchedHashAndPassword is the standard 'wrong password' result: the derived hash from the supplied password does not equal the stored hash. CompareArgon2id, CompareArgon2i and ComparePbkdf2 return it after a successful parse and constant-time comparison that failed.
Source
Thrown at oryx/hasherx/hasher_argon2.go:26
"fmt"
"math"
"time"
"github.com/ory/x/otelx"
"github.com/inhies/go-bytesize"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"github.com/pkg/errors"
"golang.org/x/crypto/argon2"
)
var (
ErrInvalidHash = errors.New("the encoded hash is not in the correct format")
ErrIncompatibleVersion = errors.New("incompatible version of argon2")
ErrMismatchedHashAndPassword = errors.New("passwords do not match")
)
type (
// Argon2Config is the configuration for a Argon2 hasher.
Argon2Config struct {
// Memory is the amount of memory to use.
Memory bytesize.ByteSize `json:"memory"`
// Iterations is the number of iterations to use.
Iterations uint32 `json:"iterations"`
// Parallelism is the number of threads to use.
Parallelism uint8 `json:"parallelism"`
// SaltLength is the length of the salt to use.
SaltLength uint32 `json:"salt_length"`
// KeyLength is the length of the key to use.View on GitHub (pinned to 4174065ffb)
Solutions
- Return a generic invalid-credentials response to the client (do not leak which factor failed).
- Verify the submitted password is preprocessed identically to registration (same trimming, casing, normalization/encoding).
- Check the hash belongs to the account being authenticated (no cross-user hash mixups in migrations).
- If the user forgot the password, trigger the password-reset flow rather than retrying.
Example fix
// before
if err := hasherx.Compare(ctx, []byte(pw), user.Hash); err != nil {
return err // leaks mismatch to caller
}
// after
if err := hasherx.Compare(ctx, []byte(pw), user.Hash); err != nil {
if errors.Is(err, hasherx.ErrMismatchedHashAndPassword) {
return ErrInvalidCredentials // generic 401
}
return err
} Defensive patterns
Strategy: try-catch
Type guard
func isMismatch(err error) bool { return errors.Is(err, hasherx.ErrMismatchedHashAndPassword) } Try / catch
if err := hasherx.Compare(ctx, []byte(pw), user.Hash); err != nil {
if errors.Is(err, hasherx.ErrMismatchedHashAndPassword) {
return http401InvalidCredentials() // generic, no user enumeration
}
return err // parse/algorithm problems surface separately
} Prevention
- Normalize password input identically at registration and login (trim, encoding)
- Apply rate limiting/lockout on repeated mismatches
- After password resets, invalidate sessions holding old credentials
- Check migrations do not mix hashes across accounts
When it happens
Trigger: Calling Compare/CompareArgon2id/CompareArgon2i/ComparePbkdf2 with a password whose recomputed hash differs from the stored hash (comparator code paths at hash_comparator.go:71/96).
Common situations: End users typing wrong passwords; passwords changed server-side (password reset) while old sessions retry old credentials; hashes and salts copied between accounts during data migrations; case/whitespace differences from trimming inconsistencies at registration vs login.
Related errors
- ErrUnknownHashAlgorithm
- ErrInvalidHash
- ErrIncompatibleVersion
- cannot open AEAD
- cookiex: at least one secret is required
AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03).
Data as JSON: /api/errors/fa42215a7cd0472f.
Report an issue: GitHub.