ory/hydra · error

ErrInvalidHash

ErrInvalidHash

Error message

the encoded hash is not in the correct format

What it means

ErrInvalidHash indicates the encoded password hash string does not match the expected PHC/modular-crypt format for its algorithm (wrong field count, malformed base64, unparsable parameters). decodeArgon2idHash/decodePbkdf2Hash return it, and CompareArgon2id/CompareArgon2i/Compare also raise it when decoded parameters are out of range (e.g. Memory > math.MaxUint32).

Source

Thrown at oryx/hasherx/hasher_argon2.go:24

	"crypto/rand"
	"encoding/base64"
	"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"`

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Verify the stored hash is complete and matches the expected format, e.g. $argon2id$v=19$m=65536,t=3,p=4$<salt>$<hash> with valid base64 segments.
  2. Regenerate the hash with the library's own Hasher/Generate function and re-store it.
  3. Check DB column length/encoding so the hash is not truncated or transformed in transit.
  4. Clamp or fix generation parameters so Memory fits in uint32 (< 4294967296 KiB).

Example fix

// before
hash := user.PasswordHash[:32] // truncated in log copy
err := hasherx.Compare(ctx, pw, []byte(hash)) // ErrInvalidHash
// after
err := hasherx.Compare(ctx, pw, []byte(user.PasswordHash)) // full stored value
Defensive patterns

Strategy: validation

Validate before calling

func looksLikePHCHash(s string) bool {
  return strings.Count(s, "$") >= 5 && strings.HasPrefix(s, "$argon2") || strings.HasPrefix(s, "$pbkdf2")
}
// verify stored hash shape and length before Compare

Type guard

func isParseableArgon2Hash(h []byte) bool {
  return strings.HasPrefix(string(h), "$argon2id$") || strings.HasPrefix(string(h), "$argon2i$")
}

Try / catch

if err := hasherx.Compare(ctx, pw, hash); err != nil {
  if errors.Is(err, hasherx.ErrInvalidHash) {
    // corrupt/truncated hash: force password reset, log the account
    return ErrCorruptCredentialRecord
  }
  return err
}

Prevention

When it happens

Trigger: Calling CompareArgon2id/CompareArgon2i/ComparePbkdf2 (or Compare dispatching to them) with a hash string whose segments cannot be parsed — missing '$' fields, corrupt base64 parts — or whose decoded argon2 memory parameter exceeds math.MaxUint32 (hash_comparator.go:59/84).

Common situations: Hashes truncated by a fixed-width DB column, hashes re-encoded through JSON/base64 round trips, hand-written fixture hashes with wrong parameter ordering, or hashes generated by tools with memory settings that overflow the uint32 check.

Related errors


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