nats-io/nats-server · error

system_account in config and operator JWT must be identical

Error message

system_account in config and operator JWT must be identical

What it means

When a system_account is set in the config, at least one operator JWT must declare the same system account. This error is thrown when operators define system accounts but none matches the config value, indicating an inconsistent trust setup.

Source

Thrown at server/jwt.go:116

		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")
		}
	} else if o.TrustedOperators[0].SystemAccount == _EMPTY_ {
		// In case the system account is neither defined in config nor in the first operator.
		// If it would be needed due to the nats account resolver, raise an error.
		switch o.AccountResolver.(type) {
		case *DirAccResolver, *CacheDirAccResolver:
			return fmt.Errorf("using nats based account resolver - the system account needs to be specified in configuration or the operator jwt")
		}
	}

	srvMajor, srvMinor, srvUpdate, _ := versionComponents(VERSION)
	for _, opc := range o.TrustedOperators {
		if major, minor, update, err := jwt.ParseServerVersion(opc.AssertServerVersion); err != nil {
			return fmt.Errorf("operator %s expects version %s got error instead: %s",
				opc.Subject, opc.AssertServerVersion, err)
		} else if major > srvMajor {
			return fmt.Errorf("operator %s expected major version %d > server major version %d",
				opc.Subject, major, srvMajor)

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Make the config `system_account` identical to the one in the operator JWT(s)
  2. Regenerate operator JWTs with the correct system account: `nsc edit operator --system-account AD...` then redeploy the updated JWT

Example fix

// before
system_account: ADOLD...
// after (match the operator JWT's system account)
system_account: ADNEW...
Defensive patterns

Strategy: validation

Validate before calling

// Go: ensure operator system accounts match config
for _, op := range o.TrustedOperators {
    if op.SystemAccount != "" && o.SystemAccount != "" && op.SystemAccount != o.SystemAccount {
        return fmt.Errorf("system account mismatch with operator")
    }
}

Prevention

When it happens

Trigger: Config has `system_account: AD...` and one or more operator JWTs whose SystemAccount is non-empty but different (or none equal to the config value); validateOptions fails.

Common situations: Re-keying or recreating the system account with nsc and forgetting to update either the config or re-issue operator JWTs; copying an operator JWT from another deployment.

Related errors


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