nats-io/nats-server · error
not trusted
Error message
not trusted
What it means
In nats-server, when a resolver processes an account-delete operator claim, it verifies the claim is self-signed by the operator and that the operator's public key appears in the server's trusted keys map (store.operator). The 'not trusted' error is returned when the deleting key's issuer is not among the server's configured trusted operator keys, so the delete request is rejected as coming from an untrusted operator.
Source
Thrown at server/accounts.go:4373
}
}
func handleDeleteRequest(store *DirJWTStore, s *Server, msg []byte, reply string) {
var accIds []any
var subj, sysAccName string
if sysAcc := s.SystemAccount(); sysAcc != nil {
sysAccName = sysAcc.GetName()
}
// Only operator and operator signing key are allowed to delete
gk, err := jwt.DecodeGeneric(string(msg))
if err == nil {
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 {View on GitHub (pinned to 3a66a489d2)
Solutions
- Sign the delete claim with a signing key listed in the server's trusted operator keys (TrustedOperators / resolver operator config).
- Add the operator public key that issued the claim to the server's operator config and reload/restart.
- Regenerate the claim with the current operator key (nsc) so subject == issuer and the issuer is trusted.
Example fix
// before erp, _ := nkeys.FromSeed(oldOperatorSeed) claim.JWT, _ = claim.Encode(erp) // signed with untrusted key // after erp, _ := nkeys.FromSeed(currentOperatorSeed) // key trusted by server config claim.JWT, _ = claim.Encode(erp)
Defensive patterns
Strategy: validation
Validate before calling
pub, err := nkeys.FromPublicKey(operatorPubKey)
if err != nil || operatorPubKey != claim.Issuer {
return fmt.Errorf("operator key %s not trusted by server", claim.Issuer)
} Try / catch
if err := pushDeleteClaim(claim); err != nil {
if strings.Contains(err.Error(), "not trusted") {
log.Fatalf("claim issuer %q not in server trusted keys; re-sign with trusted operator key", claim.Issuer)
}
} Prevention
- Always sign operator claims with the key listed in the server's operator config
- Keep operator signing keys in sync across clusters and config management
- Decode the claim and assert Subject == Issuer before pushing
When it happens
Trigger: Calling the account-delete resolver flow with a delete claim whose Issuer (the operator signing key) is not present in the server's trusted operators / configured operator keys map. Also occurs when the server was configured with a different operator key than the one that signed the claim, or TrustedOperators changed after the claim was signed.
Common situations: Rotating operator signing keys without updating the server config; pointing a server at a different operator's resolver; using a dev key to sign claims for a production server; mixed-version clustered servers with different trusted key sets.
Related errors
- auth callout violation: auth callout response is not for exp
- auth callout violation: auth callout response is not for ser
- auth callout signing key is unknown
- malformed request
- account jwt not found
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/04f771ca48b79e8d.
Report an issue: GitHub.