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

  1. Return a generic invalid-credentials response to the client (do not leak which factor failed).
  2. Verify the submitted password is preprocessed identically to registration (same trimming, casing, normalization/encoding).
  3. Check the hash belongs to the account being authenticated (no cross-user hash mixups in migrations).
  4. 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

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


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