nats-io/nats-server · warning

subject does not match jwt content

Error message

subject does not match jwt content

What it means

This ad-hoc error is created in server/events.go in the account claims update handler when the decoded account claims JWT's subject does not match the account public key used in the update request (claim.Subject != pubKey). The server replies with 'jwt update resulted in error' and this reason instead of applying the claims.

Source

Thrown at server/events.go:1667

		return
	}
	var pubKey string
	toks := strings.Split(subject, tsep)
	if len(toks) == accUpdateTokensNew {
		pubKey = toks[accReqAccIndex]
	} else if len(toks) == accUpdateTokensOld {
		pubKey = toks[accUpdateAccIdxOld]
	} else {
		s.Debugf("Received account claims update on bad subject %q", subject)
		return
	}
	if len(msg) == 0 {
		err := errors.New("request body is empty")
		respondToUpdate(s, resp, pubKey, "jwt update error", err)
	} else if claim, err := jwt.DecodeAccountClaims(string(msg)); err != nil {
		respondToUpdate(s, resp, pubKey, "jwt update resulted in error", err)
	} else if claim.Subject != pubKey {
		err := errors.New("subject does not match jwt content")
		respondToUpdate(s, resp, pubKey, "jwt update resulted in error", err)
	} else if v, ok := s.accounts.Load(pubKey); !ok {
		respondToUpdate(s, resp, pubKey, "jwt update skipped", nil)
	} else if err := s.updateAccountWithClaimJWT(v.(*Account), string(msg)); err != nil {
		respondToUpdate(s, resp, pubKey, "jwt update resulted in error", err)
	} else {
		respondToUpdate(s, resp, pubKey, "jwt updated", nil)
	}
}

// processRemoteServerShutdown will update any affected accounts.
// Will update the remote count for clients.
// Lock assume held.
func (s *Server) processRemoteServerShutdown(sid string) {
	s.accounts.Range(func(k, v any) bool {
		v.(*Account).removeRemoteServer(sid)
		return true
	})

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Verify that the JWT's claims.Subject equals the account public key embedded in the system update request subject.
  2. Re-issue the account claims JWT with the correct subject (nsc) and republish the update.
  3. Fix automation that pairs JWTs with update subjects so it derives the subject from the JWT contents.

Example fix

// before
nc.Publish(fmt.Sprintf(updateSubjTmpl, otherAccPubKey), jwtBytes)
// after
claims, _ := jwt.DecodeAccountClaims(string(jwtBytes))
nc.Publish(fmt.Sprintf(updateSubjTmpl, claims.Subject), jwtBytes)
Defensive patterns

Strategy: validation

Validate before calling

claims, err := jwt.DecodeAccountClaims(string(jwtBytes))
if err != nil || claims.Subject != accountPubKey {
    return fmt.Errorf("jwt subject %q does not match account %q", claims.Subject, accountPubKey)
}

Try / catch

if resp.Error == "jwt update resulted in error" && strings.Contains(resp.Reason, "subject does not match") {
    log.Printf("wrong account key used for update: %s", resp.Reason)
}

Prevention

When it happens

Trigger: Publishing an account claims update where the JWT inside the message decodes to a claim whose Subject field differs from the pubKey token extracted from the request subject (jwt.DecodeAccountClaims succeeds but subject mismatch).

Common situations: Signing/issuing an account JWT for one account but publishing the update under another account's public key; copy-paste of the wrong public key in the update subject; operator tooling mixing up accounts when pushing multiple JWTs.

Related errors


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