{"record":{"id":"965fd2a5c77dc2b2","repo":"nats-io/nats-server","slug":"not-self-signed","errorCode":null,"errorMessage":"not self signed","messagePattern":"not self signed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/accounts.go","lineNumber":4371,"sourceCode":"\t\tresponse := map[string]any{\"server\": server, \"data\": accIds}\n\t\ts.sendInternalMsgLocked(reply, _EMPTY_, server, response)\n\t}\n}\n\nfunc handleDeleteRequest(store *DirJWTStore, s *Server, msg []byte, reply string) {\n\tvar accIds []any\n\tvar subj, sysAccName string\n\tif sysAcc := s.SystemAccount(); sysAcc != nil {\n\t\tsysAccName = sysAcc.GetName()\n\t}\n\t// Only operator and operator signing key are allowed to delete\n\tgk, err := jwt.DecodeGeneric(string(msg))\n\tif err == nil {\n\t\tsubj = gk.Subject\n\t\tif store.deleteType == NoDelete {\n\t\t\terr = fmt.Errorf(\"delete must be enabled in server config\")\n\t\t} else if subj != gk.Issuer {\n\t\t\terr = fmt.Errorf(\"not self signed\")\n\t\t} else if _, ok := store.operator[gk.Issuer]; !ok {\n\t\t\terr = fmt.Errorf(\"not trusted\")\n\t\t} else if list, ok := gk.Data[\"accounts\"]; !ok {\n\t\t\terr = fmt.Errorf(\"malformed request\")\n\t\t} else if accIds, ok = list.([]any); !ok {\n\t\t\terr = fmt.Errorf(\"malformed request\")\n\t\t} else {\n\t\t\tfor _, entry := range accIds {\n\t\t\t\tif acc, ok := entry.(string); !ok ||\n\t\t\t\t\tacc == _EMPTY_ || !nkeys.IsValidPublicAccountKey(acc) {\n\t\t\t\t\terr = fmt.Errorf(\"malformed request\")\n\t\t\t\t\tbreak\n\t\t\t\t} else if acc == sysAccName {\n\t\t\t\t\terr = fmt.Errorf(\"not allowed to delete system account\")\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}","sourceCodeStart":4353,"sourceCodeEnd":4389,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/accounts.go#L4353-L4389","documentation":"Delete requests on the account resolver must be signed by the account itself (subject == issuer). If the decoded delete JWT's Subject differs from its Issuer — e.g. it was signed by an operator or signing key rather than the account key — the server rejects it with 'not self signed'.","triggerScenarios":"Publishing a delete request JWT whose issuer is an operator or operator signing key while the subject names an account, so gk.Subject != gk.Issuer; using the wrong nsc/operator context that signs with the operator key.","commonSituations":"Automation signing delete requests with operator credentials; using an operator-signed delete where account-signed is required; misconfigured nsc signing keys.","solutions":["Sign the delete request with the account's own key pair so Issuer equals Subject","Check nsc/operator setup so the delete is issued from the account context, not the operator","Verify the JWT (decode and compare subject/issuer) before publishing the delete request"],"exampleFix":"// before\nnsc delete account --sign-with-operator-key ... // operator-signed, rejected\n// after\nnsc delete account my-account // signed by the account key itself (issuer == subject)","handlingStrategy":"validation","validationCode":"gk, err := jwt.DecodeGeneric(deleteJWT)\nif err != nil {\n    return err\n}\nif gk.Subject != gk.Issuer {\n    return fmt.Errorf(\"delete request must be self-signed (subject %q != issuer %q)\", gk.Subject, gk.Issuer)\n}\n","typeGuard":null,"tryCatchPattern":"if err := sendDeleteRequest(store, msg); err != nil {\n    if strings.Contains(err.Error(), \"not self signed\") {\n        // re-sign with the account key and resend\n    }\n}\n","preventionTips":["Sign delete requests with the account's own keypair","Verify subject==issuer in tooling before publishing","Avoid operator-key signing contexts (nsc) for account deletes"],"tags":["nats-server","jwt","account-resolver","signature"],"backgroundTag":"jwt-not-self-signed","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}