{"record":{"id":"3cb62f4ec44a0e48","repo":"nats-io/nats-server","slug":"subject-does-not-match-jwt-content","errorCode":null,"errorMessage":"subject does not match jwt content","messagePattern":"subject does not match jwt content","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/accounts.go","lineNumber":4529,"sourceCode":"\tfor _, reqSub := range []string{accUpdateEventSubjOld, accUpdateEventSubjNew} {\n\t\t// subscribe to account jwt update requests\n\t\tif _, err := s.sysSubscribe(fmt.Sprintf(reqSub, \"*\"), func(_ *subscription, _ *client, _ *Account, subj, resp string, msg []byte) {\n\t\t\tvar pubKey string\n\t\t\ttk := strings.Split(subj, tsep)\n\t\t\tif len(tk) == accUpdateTokensNew {\n\t\t\t\tpubKey = tk[accReqAccIndex]\n\t\t\t} else if len(tk) == accUpdateTokensOld {\n\t\t\t\tpubKey = tk[accUpdateAccIdxOld]\n\t\t\t} else {\n\t\t\t\ts.Debugf(\"DirResolver - jwt update skipped due to bad subject %q\", subj)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif claim, err := jwt.DecodeAccountClaims(string(msg)); err != nil {\n\t\t\t\trespondToUpdate(s, resp, \"n/a\", \"jwt update resulted in error\", err)\n\t\t\t} else if err := claimValidate(claim); err != nil {\n\t\t\t\trespondToUpdate(s, resp, claim.Subject, \"jwt validation failed\", err)\n\t\t\t} else if claim.Subject != pubKey {\n\t\t\t\terr := errors.New(\"subject does not match jwt content\")\n\t\t\t\trespondToUpdate(s, resp, pubKey, \"jwt update resulted in error\", err)\n\t\t\t} else if claim.Issuer == op && strict {\n\t\t\t\terr := errors.New(\"operator requires issuer to be a signing key\")\n\t\t\t\trespondToUpdate(s, resp, pubKey, \"jwt update resulted in error\", err)\n\t\t\t} else if err := dr.save(pubKey, string(msg)); err != nil {\n\t\t\t\trespondToUpdate(s, resp, pubKey, \"jwt update resulted in error\", err)\n\t\t\t} else {\n\t\t\t\trespondToUpdate(s, resp, pubKey, \"jwt updated\", nil)\n\t\t\t}\n\t\t}); err != nil {\n\t\t\treturn fmt.Errorf(\"error setting up update handling: %v\", err)\n\t\t}\n\t}\n\tif _, err := s.sysSubscribe(accClaimsReqSubj, func(_ *subscription, c *client, _ *Account, _, resp string, msg []byte) {\n\t\t// As this is a raw message, we need to extract payload and only decode claims from it,\n\t\t// in case request is sent with headers.\n\t\t_, msg = c.msgParts(msg)\n\t\tif claim, err := jwt.DecodeAccountClaims(string(msg)); err != nil {","sourceCodeStart":4511,"sourceCodeEnd":4547,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/accounts.go#L4511-L4547","documentation":"consumerMemStore.ForceUpdate performs the same sanity checks as Update but skips the normal state-validity comparison against current state; it still rejects a ConsumerState whose AckFloor.Consumer exceeds Delivered.Consumer. The invariant is fundamental, so even forced updates cannot bypass it.","triggerScenarios":"Calling consumerMemStore.ForceUpdate(state) where state.AckFloor.Consumer > state.Delivered.Consumer — typically during consumer state restore or leader takeover in a cluster.","commonSituations":"JetStream consumer state sync/restore from a peer with corrupt or reordered data; hand-built state in tests or migration tooling.","solutions":["Sanitize the state (clamp consumer ack floor to delivered) before ForceUpdate","Reject the incoming state at the sync layer instead of forcing it","Re-fetch a consistent consumer state snapshot from the current leader"],"exampleFix":"// before\nconsumer.ForceUpdate(badState) // AckFloor.Consumer > Delivered.Consumer\n// after\nif badState.AckFloor.Consumer > badState.Delivered.Consumer {\n    badState.AckFloor.Consumer = badState.Delivered.Consumer\n}\nconsumer.ForceUpdate(badState)","handlingStrategy":"validation","validationCode":"func validConsumerAckFloor(st *ConsumerState) bool {\n    return st.AckFloor.Consumer <= st.Delivered.Consumer &&\n        st.AckFloor.Stream <= st.Delivered.Stream\n}\nif !validConsumerAckFloor(st) { return ErrInvalidState }\nconsumer.ForceUpdate(st)","typeGuard":null,"tryCatchPattern":"if err := consumer.ForceUpdate(st); err != nil {\n    if strings.Contains(err.Error(), \"bad ack floor for consumer\") {\n        st.AckFloor.Consumer = st.Delivered.Consumer\n        err = consumer.ForceUpdate(st)\n    }\n}","preventionTips":["Sanitize replicated states at the sync boundary before ForceUpdate","Share one invariant-check helper for Update and ForceUpdate","Log the offending state when clamping to aid cluster debugging"],"tags":["jetstream","consumer-state","force-update"],"backgroundTag":"invalid-ack-floor","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}