netbirdio/netbird · error

incompatible argon2 version

Error message

incompatible argon2 version

What it means

Sentinel error argon2id.ErrIncompatibleVersion (shared/hash/argon2id/argon2id.go:27), returned by decodeHash when the stored PHC string parses but its v= number differs from golang.org/x/crypto/argon2.Version (0x13, the only version this package produces and accepts). The verification is intentionally strict: the package will not attempt to verify hashes from other Argon2 versions.

Source

Thrown at shared/hash/argon2id/argon2id.go:27

	"strings"

	"golang.org/x/crypto/argon2"
)

const (
	argon2Memory      = 19456
	argon2Iterations  = 2
	argon2Parallelism = 1
	argon2SaltLength  = 16
	argon2KeyLength   = 32
)

var (
	// ErrInvalidHash is returned when the hash string format is invalid
	ErrInvalidHash = errors.New("invalid hash format")

	// ErrIncompatibleVersion is returned when the Argon2 version is not supported
	ErrIncompatibleVersion = errors.New("incompatible argon2 version")

	// ErrMismatchedHashAndPassword is returned when password verification fails
	ErrMismatchedHashAndPassword = errors.New("password does not match hash")
)

func Hash(secret string) (string, error) {
	salt := make([]byte, argon2SaltLength)
	if _, err := rand.Read(salt); err != nil {
		return "", fmt.Errorf("failed to generate salt: %w", err)
	}

	hash := argon2.IDKey(
		[]byte(secret),
		salt,
		argon2Iterations,
		argon2Memory,
		argon2Parallelism,
		argon2KeyLength,

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Re-hash the secret with this package (argon2id.Hash) and replace the stored value, since cross-version verification is unsupported
  2. If the hash comes from an import, verify it once with the originating library (if version-compatible semantics matter) and rehash on first successful login
  3. For tests, always generate fixtures with argon2id.Hash so the embedded version matches

Example fix

// before: imported hash with old argon2 version
stored := "$argon2id$v=16$m=19456,t=2,p=1$...$..." // Verify -> incompatible argon2 version

// after: reset/rehash with the current package
stored, err := argon2id.Hash(password)
Defensive patterns

Strategy: fallback

Validate before calling

func hashVersion(encodedHash string) (int, error) {
    parts := strings.Split(encodedHash, "$")
    if len(parts) != 6 { return 0, argon2id.ErrInvalidHash }
    var v int
    if _, err := fmt.Sscanf(parts[2], "v=%d", &v); err != nil {
        return 0, fmt.Errorf("%w: invalid version", argon2id.ErrInvalidHash)
    }
    return v, nil
}

Try / catch

if err := argon2id.Verify(password, stored); err != nil {
    if errors.Is(err, argon2id.ErrIncompatibleVersion) {
        // cannot verify cross-version: fall back to reset/rehash flow
        newHash, herr := argon2id.Hash(password)
        if herr != nil { return herr }
        return store.UpdateHash(userID, newHash) // requires authenticated reset or migration path
    }
    return err
}

Prevention

When it happens

Trigger: Calling argon2id.Verify against a hash generated with a different Argon2 version, e.g. v=16 (Argon2 v1.2-era tooling, passlib older settings, or a PHP/Python implementation writing v=16 hashes).

Common situations: Importing credential dumps from systems whose Argon2 library emits v=16; interoperating with frameworks whose default argon2 version differs; hand-constructed hash strings.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/460154a62ef81743. Report an issue: GitHub.