nats-io/nats-server · error
operators do not allow Accounts to be configured directly
Error message
operators do not allow Accounts to be configured directly
What it means
In operator mode, accounts are defined by account JWTs resolved via the AccountResolver, not by the config file. This error is thrown when `accounts` entries appear in a config that also sets an operator, since static account definitions conflict with decentralized JWT account management.
Solutions
- Remove the `accounts` block from the config and serve account JWTs through the resolver (resolver_preload / directory / URL)
- If static accounts are actually required, remove the `operator` setting to stay in classic mode
Example fix
// before
operator: eyJ...
accounts: { A: { users: [...] } }
// after
operator: eyJ...
resolver: MEMORY
resolver_preload: { AD...: eyJ... } Defensive patterns
Strategy: validation
Validate before calling
// Go: reject mixed operator/static account config
if len(o.TrustedOperators) > 0 && len(o.Accounts) > 0 {
return fmt.Errorf("accounts must come from the resolver in operator mode")
} Prevention
- Strip accounts/users/nkeys blocks when migrating to operator mode
- Serve accounts via resolver_preload or nsc-generated account JWTs
- Lint configs to forbid `accounts` when `operator` is present
When it happens
Trigger: Config contains both `operator: <jwt>` and an `accounts: {...}` block; validateOptions rejects it at startup.
Common situations: Migrating an existing nats-server config from account/user mode to operator mode and leaving the old accounts block in place; merging sample operator configs with legacy account configs.
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 users 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/0cd43c060f0321cb.
Report an issue: GitHub.
Appendix: source
Thrown at server/jwt.go:95
}
if o.DefaultSentinel != _EMPTY_ {
juc, err := jwt.DecodeUserClaims(o.DefaultSentinel)
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
}View on GitHub (pinned to 3a66a489d2)