nats-io/nats-server · error
not allowed to delete system account
Error message
not allowed to delete system account
What it means
The resolver explicitly refuses to delete the system account (acc == sysAccName) and returns 'not allowed to delete system account'. This is a deliberate safety guard protecting the SYS account that nats-server relies on for internal messaging/monitoring.
Source
Thrown at server/accounts.go:4385
subj = gk.Subject
if store.deleteType == NoDelete {
err = fmt.Errorf("delete must be enabled in server config")
} else if subj != gk.Issuer {
err = fmt.Errorf("not self signed")
} else if _, ok := store.operator[gk.Issuer]; !ok {
err = fmt.Errorf("not trusted")
} else if list, ok := gk.Data["accounts"]; !ok {
err = fmt.Errorf("malformed request")
} else if accIds, ok = list.([]any); !ok {
err = fmt.Errorf("malformed request")
} else {
for _, entry := range accIds {
if acc, ok := entry.(string); !ok ||
acc == _EMPTY_ || !nkeys.IsValidPublicAccountKey(acc) {
err = fmt.Errorf("malformed request")
break
} else if acc == sysAccName {
err = fmt.Errorf("not allowed to delete system account")
break
}
}
}
}
if err != nil {
respondToUpdate(s, reply, _EMPTY_, fmt.Sprintf("delete accounts request by %s failed", subj), err)
return
}
errs := []string{}
passCnt := 0
for _, acc := range accIds {
if err := store.delete(acc.(string)); err != nil {
errs = append(errs, err.Error())
} else {
passCnt++
}
}View on GitHub (pinned to 3a66a489d2)
Solutions
- Remove the system account public key from the claim's accounts list before publishing.
- Filter out the key returned by the server's system_account config in your automation.
- If SYS must be replaced, provision a new resolver/operator setup rather than deleting SYS.
Example fix
// before
accounts := allAccounts // includes system account key
// after
accounts := slices.DeleteFunc(allAccounts, func(a string) bool { return a == sysAccountPubKey }) Defensive patterns
Strategy: validation
Validate before calling
sysKey := serverSystemAccountPubKey // from server config
for _, a := range accounts {
if a == sysKey {
return fmt.Errorf("refusing to delete system account %s", a)
}
} Try / catch
if err := pushClaim(jwt); err != nil && strings.Contains(err.Error(), "not allowed to delete system account") {
log.Fatalf("remove SYS account key from delete list; it is protected")
} Prevention
- Exclude the system account key in bulk-delete automation
- Read the system_account key from server config rather than hardcoding
- Audit delete claims before publishing
When it happens
Trigger: Submitting a delete operator claim whose accounts list includes the system account's public key (the key matching the server's system account, e.g. from system_account config).
Common situations: Bulk cleanup scripts that 'delete all accounts' including SYS; operators trying to remove a compromised environment wholesale; scripts copying the full account list from the resolver without excluding SYS.
Related errors
- system account not setup
- not trusted
- malformed request
- system_account in config and operator JWT must be identical
- using nats based account resolver - the system account needs
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/e3af2c5fba497823.
Report an issue: GitHub.