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

  1. Keep the leafnodes users array identical in the reloaded config, or restart the server to apply user changes.
  2. Change only supported leaf node fields (URLs, TLS, etc.) during reload; move user changes to a full restart/maintenance window.
  3. 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

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


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