nats-io/nats-server · error

jwt subject nkey and provided nkey do not match

Error message

jwt subject nkey and provided nkey do not match

What it means

During saveIfNewer, after decoding the existing and new JWTs, the store checks that the new JWT's subject claim matches the public key the caller supplied as the storage key. A mismatch means the caller is trying to file a JWT under a key that does not own it, which would corrupt the resolver's key-to-JWT mapping.

Source

Thrown at server/dirstore.go:535

	dirPath := filepath.Dir(path)
	if _, err := validateDirPath(dirPath); err != nil {
		if err := os.MkdirAll(dirPath, defaultDirPerms); err != nil {
			return err
		}
	}
	if _, err := os.Stat(path); err == nil {
		if newJWT, err := jwt.DecodeGeneric(theJWT); err != nil {
			return err
		} else if existing, err := os.ReadFile(path); err != nil {
			return err
		} else if existingJWT, err := jwt.DecodeGeneric(string(existing)); err != nil {
			// skip if it can't be decoded
		} else if existingJWT.ID == newJWT.ID {
			return nil
		} else if existingJWT.IssuedAt > newJWT.IssuedAt {
			return nil
		} else if newJWT.Subject != publicKey {
			return fmt.Errorf("jwt subject nkey and provided nkey do not match")
		} else if existingJWT.Subject != newJWT.Subject {
			return fmt.Errorf("subject of existing and new jwt do not match")
		}
	}
	store.Lock()
	cb := store.changed
	changed, err := store.write(path, publicKey, theJWT)
	store.Unlock()
	if err != nil {
		return err
	} else if changed && cb != nil {
		cb(publicKey)
	}
	return nil
}

func xorAssign(lVal *[sha256.Size]byte, rVal [sha256.Size]byte) {
	for i := range rVal {

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Ensure the key passed to Store/saveIfNewer equals newJWT.Subject as decoded from the JWT
  2. Decode the JWT first and use its claims.Subject as the storage key
  3. Audit Merge callers that zip external key lists with JWT lists for off-by-one/mispairing

Example fix

// before
store.Store(issuerKey, accJWT) // error: jwt subject nkey and provided nkey do not match
// after
claims, _ := jwt.DecodeAccountClaims(accJWT)
store.Store(claims.Subject, accJWT)
Defensive patterns

Strategy: validation

Validate before calling

claims, err := jwt.DecodeAccountClaims(theJWT)
if err != nil { return err }
if claims.Subject != publicKey { return fmt.Errorf("key %q does not own jwt subject %q", publicKey, claims.Subject) }
err = store.Store(publicKey, theJWT)

Try / catch

if err := store.Store(pub, jwt); err != nil && strings.Contains(err.Error(), "do not match") {
    log.Printf("jwt/key mismatch for %s", pub)
}

Prevention

When it happens

Trigger: Calling Store/Merge where publicKey != the Subject claim of theJWT (after the existing-JWT short-circuits for identical ID or older IssuedAt).

Common situations: Passing an issuer key instead of the subject key; storing activation JWTs under the account key rather than the correct subject; bug in batch-merge code pairing keys with the wrong JWTs.

Related errors


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/e321bbce5abdb5f5. Report an issue: GitHub.