{"record":{"id":"5293f5676a7d30e6","repo":"nats-io/nats-server","slug":"operator-requires-issuer-to-be-a-signing-key","errorCode":null,"errorMessage":"operator requires issuer to be a signing key","messagePattern":"operator requires issuer to be a signing key","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/accounts.go","lineNumber":4532,"sourceCode":"\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 {\n\t\t\trespondToUpdate(s, resp, \"n/a\", \"jwt update resulted in error\", err)\n\t\t} else if claim.Issuer == op && strict {\n\t\t\terr := errors.New(\"operator requires issuer to be a signing key\")","sourceCodeStart":4514,"sourceCodeEnd":4550,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/accounts.go#L4514-L4550","documentation":"The stream-namespace counterpart of the ForceUpdate consumer-ack-floor check: rejects a state where AckFloor.Stream > Delivered.Stream. ForceUpdate is used in cluster state synchronization, so this error typically surfaces when replicated state is inconsistent.","triggerScenarios":"Calling consumerMemStore.ForceUpdate(state) where state.AckFloor.Stream > state.Delivered.Stream.","commonSituations":"Leader election replaying inconsistent raft state; restore-from-backup with mismatched sequence counters; test fixtures with hand-written ConsumerState.","solutions":["Clamp AckFloor.Stream to Delivered.Stream before ForceUpdate","Fix the state replication/replay logic that produced floor > delivered","Rebuild state from the underlying stream message sequences"],"exampleFix":"// before\nst.AckFloor.Stream, st.Delivered.Stream = 900, 800\nconsumer.ForceUpdate(st)\n// after\nst.AckFloor.Stream = min(st.AckFloor.Stream, st.Delivered.Stream)\nconsumer.ForceUpdate(st)","handlingStrategy":"validation","validationCode":"if st.AckFloor.Stream > st.Delivered.Stream {\n    return fmt.Errorf(\"replicated state inconsistent: stream ack floor %d > delivered %d\",\n        st.AckFloor.Stream, st.Delivered.Stream)\n}\nconsumer.ForceUpdate(st)","typeGuard":null,"tryCatchPattern":"if err := consumer.ForceUpdate(st); err != nil {\n    if strings.Contains(err.Error(), \"bad ack floor for stream\") {\n        // request fresh state snapshot from leader instead of forcing\n    }\n}","preventionTips":["Verify sequence ordering in raft replay before ForceUpdate","Compare incoming state sequence ranges with local before applying","Refuse (don't clamp) state from peers in tests to catch bugs early"],"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"}