nats-io/nats-server · error
field "Users": old=%v, new=%v
Error message
field "Users": old=%v, new=%v
What it means
During config reload, getLeafNodeOptionsChanges compares old and new leafnodes{} options. The Users field cannot be compared with DeepEqual, so usersHaveChanged() is used; if the user credentials list differs, reload fails because changing leaf node users on the fly is unsupported.
Source
Thrown at server/reload.go:925
added []*RemoteLeafOpts
changed map[*leafNodeCfg]*remoteLeafOption
}
type remoteLeafOption struct {
tlsFirstChanged bool
compressionChanged bool
disabledChanged bool
opts *RemoteLeafOpts
}
// Given `old` and `new` Leafnode options, this function will return the structure
// used for applying the configuration, or an error is there are changes that
// are not supported.
func getLeafNodeOptionsChanges(s *Server, old, new *LeafNodeOpts) (*leafNodeOption, error) {
// We can't use DeepEqual for `Users` field, so do custom check.
if usersHaveChanged(old.Users, new.Users) {
return nil, fmt.Errorf("field \"Users\": old=%v, new=%v", old.Users, new.Users)
}
// Check the main leafnodes{} block to see if there are any changes that are
// not supported. We provide a list of fields to ignore (we already checked,
// allow them to be modified or will check later).
if err := checkConfigsEqual(old, new, []string{
"Compression",
"Remotes",
"TLSHandshakeFirst",
"TLSHandshakeFirstFallback",
"TLSConfig",
"Users",
}); err != nil {
return nil, err
}
const (
remoteErrFormat = "remote %s: %s"View on GitHub (pinned to 3a66a489d2)
Solutions
- Keep the leafnodes users array identical in the reloaded config, or restart the server to apply user changes.
- Change only supported leaf node fields (URLs, TLS, etc.) during reload; move user changes to a full restart/maintenance window.
- If the change was accidental, revert the users section to match the running configuration.
Example fix
// before (reload)
leafnodes { users: [ {user: a, pass: x} ] } // changed users
// after: restart instead of reload, or keep users unchanged during reload Defensive patterns
Strategy: validation
Validate before calling
// before calling Reload(), diff configs yourself
if !reflect.DeepEqual(oldLeaf.Users, newLeaf.Users) {
// schedule a full restart instead of Reload()
return errors.New("leafnodes users changed: restart required")
} Try / catch
if err := srv.Reload(); err != nil {
if strings.Contains(err.Error(), "field \"Users\"") {
log.Println("leaf users change needs a restart, not a reload")
}
} Prevention
- Keep leafnodes.users identical across reloads.
- Treat credential rotation for leaf users as a restart-level change.
- Use config linting/CI to diff user sections before issuing SIGHUP.
When it happens
Trigger: Modifying the users array of an existing leafnodes{} block (adding, removing, or changing credentials/URL users) and issuing a reload (SIGHUP or Reload()).
Common situations: Operators rotating leaf node credentials via config reload instead of a restart; adding a second remote account/user entry in a running server's leaf block.
Related errors
- remote %s: %s
- remote %s: cannot be added at the moment, try again
- OnReload, sort or explicitly skip type: %s
- unable to add account %q to the list of dedicated routes: %v
- invalid ClientAdvertise value of %s, err=%v
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/451f777fc49fbc0a.
Report an issue: GitHub.