nats-io/nats-server · error
operators do not allow users to be configured directly
Error message
operators do not allow users to be configured directly
What it means
Under operator mode, users and nkeys must come from signed account JWTs rather than the server config. This error is thrown when `users` or `nkeys` are listed directly in a config that also declares an operator, because that bypasses operator signing of credentials.
Solutions
- Remove the `users`/`nkeys` blocks and issue user JWTs signed by the account (nsc add user) served via the resolver
- If direct users are needed, remove the `operator` setting and use classic authentication mode
Example fix
// before
operator: eyJ...
users: [{user: alice, password: pwd}]
// after
operator: eyJ...
resolver: MEMORY
// create alice's credentials with: nsc add user --name alice Defensive patterns
Strategy: validation
Validate before calling
// Go: reject direct users/nkeys in operator mode
if len(o.TrustedOperators) > 0 && (len(o.Users) > 0 || len(o.Nkeys) > 0) {
return fmt.Errorf("issue user credentials via nsc instead")
} Prevention
- Use `nsc add user` to issue credentials instead of config users
- Remove legacy users/nkeys blocks during operator migration
- Add config linting to catch this combination
When it happens
Trigger: Config has `operator: <jwt>` plus a top-level `users: [...]` or `nkeys: [...]` array; validateOptions aborts startup.
Common situations: Keeping old user credentials in the config after adding an operator; testing operator mode by appending it to an existing single-user server config.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- conflicting options for 'TrustedKeys' and 'TrustedOperators'
- operators do not allow Accounts to be configured directly
- operators require an account resolver to be configured
- operator expected major version > server major version
- operator expected minor version > server minor version
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/25b92fe8e8cd52f3.
Report an issue: GitHub.
Appendix: source
Thrown at server/jwt.go:98
if err != nil {
return fmt.Errorf("default sentinel JWT not valid")
}
if !juc.BearerToken && juc.IssuerAccount != "" && juc.HasEmptyPermissions() {
// we cannot resolve the account yet - but this looks like a scoped user
// it will be rejected at runtime if not valid
} else if !juc.BearerToken {
return fmt.Errorf("default sentinel must be a bearer token")
}
}
if o.AccountResolver == nil {
return fmt.Errorf("operators require an account resolver to be configured")
}
if len(o.Accounts) > 0 {
return fmt.Errorf("operators do not allow Accounts to be configured directly")
}
if len(o.Users) > 0 || len(o.Nkeys) > 0 {
return fmt.Errorf("operators do not allow users to be configured directly")
}
if len(o.TrustedOperators) > 0 && len(o.TrustedKeys) > 0 {
return fmt.Errorf("conflicting options for 'TrustedKeys' and 'TrustedOperators'")
}
if o.SystemAccount != _EMPTY_ {
foundSys := false
foundNonEmpty := false
for _, op := range o.TrustedOperators {
if op.SystemAccount != _EMPTY_ {
foundNonEmpty = true
}
if op.SystemAccount == o.SystemAccount {
foundSys = true
break
}
}
if foundNonEmpty && !foundSys {
return fmt.Errorf("system_account in config and operator JWT must be identical")View on GitHub (pinned to 3a66a489d2)