ory/hydra · error

ErrUnknownHashAlgorithm

ErrUnknownHashAlgorithm

Error message

unknown hash algorithm

What it means

hasherx.Compare dispatches on the hash encoding prefix ($argon2id$, $argon2i$, $2a$ bcrypt, $pbkdf2$ etc.). If the hash's algorithm identifier is none of the recognized kinds, Compare returns ErrUnknownHashAlgorithm because it has no comparator for that scheme.

Source

Thrown at oryx/hasherx/hash_comparator.go:18

package hasherx

import (
	"context"
	"crypto/subtle"
	"encoding/base64"
	"fmt"
	"math"
	"regexp"
	"strings"

	"github.com/pkg/errors"
	"golang.org/x/crypto/argon2"
	"golang.org/x/crypto/bcrypt"
	"golang.org/x/crypto/pbkdf2"
)

var ErrUnknownHashAlgorithm = errors.New("unknown hash algorithm")

// Compare the given password with the given hash.
func Compare(ctx context.Context, password []byte, hash []byte) error {
	switch {
	case IsBcryptHash(hash):
		return CompareBcrypt(ctx, password, hash)
	case IsArgon2idHash(hash):
		return CompareArgon2id(ctx, password, hash)
	case IsArgon2iHash(hash):
		return CompareArgon2i(ctx, password, hash)
	case IsPbkdf2Hash(hash):
		return ComparePbkdf2(ctx, password, hash)
	default:
		return errors.WithStack(ErrUnknownHashAlgorithm)
	}
}

func CompareBcrypt(_ context.Context, password []byte, hash []byte) error {

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Store hashes produced by a supported scheme (bcrypt, argon2id, argon2i, pbkdf2) with their full standard prefix intact.
  2. Check the stored hash string starts with a supported prefix ($2a/$2b/$2y, $argon2id$, $argon2i$, $pbkdf2) before calling Compare.
  3. Re-hash imported credentials with a supported hasher on next login (progressive migration) instead of comparing them directly.
  4. Fix storage/encoding steps that strip or mangle the prefix (e.g. URL-safe base64 rewrites).

Example fix

// before
if !strings.HasPrefix(string(storedHash), "$2") { /* raw sha256 hex */ }
err := hasherx.Compare(ctx, pw, storedHash) // -> unknown hash algorithm
// after
hash, _ := hasherx.BcryptHasher{}.Generate(ctx, pw) // or argon2id hasher
err := hasherx.Compare(ctx, pw, hash)
Defensive patterns

Strategy: validation

Validate before calling

func knownHashFormat(hash []byte) bool {
  p := string(hash)
  return strings.HasPrefix(p, "$2a$") || strings.HasPrefix(p, "$2b$") ||
    strings.HasPrefix(p, "$2y$") || strings.HasPrefix(p, "$argon2id$") ||
    strings.HasPrefix(p, "$argon2i$") || strings.HasPrefix(p, "$pbkdf2")
}
// guard before Compare

Type guard

func isSupportedHash(b []byte) bool { return knownHashFormat(b) }

Try / catch

if err := hasherx.Compare(ctx, pw, hash); err != nil {
  if errors.Is(err, hasherx.ErrUnknownHashAlgorithm) {
    // flag for re-hash/migration instead of failing auth outright
    return migrateAndRehash(user, pw)
  }
  return err
}

Prevention

When it happens

Trigger: Calling Compare(ctx, password, hash) where hash carries an unrecognized identifier (e.g. $scrypt$, a bare SHA-256 hex string, or a truncated/garbled prefix), so the prefix-based switch falls to the default branch.

Common situations: Importing users from another system using an unsupported algorithm, hashes damaged by truncation or base64 re-encoding, hand-crafted test fixtures, or hashes stored without their algorithm prefix.

Related errors


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