{"record":{"id":"03ff33d623869459","repo":"nats-io/nats-server","slug":"subject-of-existing-and-new-jwt-do-not-match","errorCode":null,"errorMessage":"subject of existing and new jwt do not match","messagePattern":"subject of existing and new jwt do not match","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/dirstore.go","lineNumber":537,"sourceCode":"\t\tif err := os.MkdirAll(dirPath, defaultDirPerms); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif _, err := os.Stat(path); err == nil {\n\t\tif newJWT, err := jwt.DecodeGeneric(theJWT); err != nil {\n\t\t\treturn err\n\t\t} else if existing, err := os.ReadFile(path); err != nil {\n\t\t\treturn err\n\t\t} else if existingJWT, err := jwt.DecodeGeneric(string(existing)); err != nil {\n\t\t\t// skip if it can't be decoded\n\t\t} else if existingJWT.ID == newJWT.ID {\n\t\t\treturn nil\n\t\t} else if existingJWT.IssuedAt > newJWT.IssuedAt {\n\t\t\treturn nil\n\t\t} else if newJWT.Subject != publicKey {\n\t\t\treturn fmt.Errorf(\"jwt subject nkey and provided nkey do not match\")\n\t\t} else if existingJWT.Subject != newJWT.Subject {\n\t\t\treturn fmt.Errorf(\"subject of existing and new jwt do not match\")\n\t\t}\n\t}\n\tstore.Lock()\n\tcb := store.changed\n\tchanged, err := store.write(path, publicKey, theJWT)\n\tstore.Unlock()\n\tif err != nil {\n\t\treturn err\n\t} else if changed && cb != nil {\n\t\tcb(publicKey)\n\t}\n\treturn nil\n}\n\nfunc xorAssign(lVal *[sha256.Size]byte, rVal [sha256.Size]byte) {\n\tfor i := range rVal {\n\t\t(*lVal)[i] ^= rVal[i]\n\t}","sourceCodeStart":519,"sourceCodeEnd":555,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/dirstore.go#L519-L555","documentation":"During saveIfNewer, the existing JWT on disk and the incoming JWT decode to different subject claims. Even though the incoming JWT matches the provided key, replacing the file would change which entity the stored JWT belongs to, so the store refuses. This guards against overwriting a JWT for one account/activation with one for another.","triggerScenarios":"Calling Store/Merge where the file at path already contains a JWT whose Subject differs from theJWT.Subject (and the earlier checks for same ID / older IssuedAt did not short-circuit).","commonSituations":"Directory reuse across operator/account migrations where old JWTs remain; re-keying an account and writing the new JWT under the old path; corrupted or swapped JWT files in the resolver dir.","solutions":["Delete or migrate stale JWTs from the store directory before re-keying subjects","Verify the existing file's claims; if it is a leftover, remove it and retry the Store/Merge","Ensure key-generation/rotation flows update the directory in step with subject changes"],"exampleFix":"// before\n// dir still holds JWT for old subject A; storing JWT for subject B under same key\nstore.Store(key, newSubjectJWT) // error: subject of existing and new jwt do not match\n// after\nstore.delete(key) // remove stale JWT for old subject first (delete-enabled store)\nstore.Store(key, newSubjectJWT)","handlingStrategy":"validation","validationCode":"existing, err := loadExistingJWT(path)\nif err == nil {\n    old, _ := jwt.DecodeAccountClaims(existing)\n    new, _ := jwt.DecodeAccountClaims(theJWT)\n    if old != nil && new != nil && old.Subject != new.Subject {\n        return fmt.Errorf(\"refusing overwrite across subjects\")\n    }\n}\nerr = store.Store(publicKey, theJWT)","typeGuard":null,"tryCatchPattern":"if err := store.Store(pub, jwt); err != nil && strings.Contains(err.Error(), \"subject of existing\") {\n    log.Printf(\"stale JWT on disk for %s; clean the store dir\", pub)\n}","preventionTips":["Clean stale JWTs after account re-keying/migrations","Keep one operator/account namespace per store directory","Monitor resolver dirs during key rotation"],"tags":["jwt","claims","store"],"backgroundTag":"jwt-subject-key-mismatch","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}