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
- Verify that the JWT's claims.Subject equals the account public key embedded in the system update request subject.
- Re-issue the account claims JWT with the correct subject (nsc) and republish the update.
- 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
- Derive the update subject's account key from the decoded JWT, never from a separate variable.
- Add a pre-publish check comparing claims.Subject with the target public key.
- Use nsc push which handles subject/JWT pairing automatically.
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
- request body is empty
- account jwt not found
- auth callout violation: auth callout response is not for exp
- auth callout violation: auth callout response is not for ser
- auth callout signing key is unknown
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/269ed5706ce49629.
Report an issue: GitHub.