{"record":{"id":"df113abff3237e76","repo":"nats-io/nats-server","slug":"claim-based-accounts-can-not-be-updated-directly","errorCode":null,"errorMessage":"claim based accounts can not be updated directly","messagePattern":"claim based accounts can not be updated directly","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/accounts.go","lineNumber":1806,"sourceCode":"\t\t\ta.mu.RLock()\n\t\t}\n\t}\n\ta.mu.RUnlock()\n\treturn nil\n}\n\n// SetServiceImportSharing will allow sharing of information about requests with the export account.\n// Used for service latency tracking at the moment.\nfunc (a *Account) SetServiceImportSharing(destination *Account, to string, allow bool) error {\n\treturn a.setServiceImportSharing(destination, to, true, allow)\n}\n\n// setServiceImportSharing will allow sharing of information about requests with the export account.\nfunc (a *Account) setServiceImportSharing(destination *Account, to string, check, allow bool) error {\n\ta.mu.Lock()\n\tdefer a.mu.Unlock()\n\tif check && a.isClaimAccount() {\n\t\treturn fmt.Errorf(\"claim based accounts can not be updated directly\")\n\t}\n\t// We can't use getServiceImportForAccountLocked() here since we are looking\n\t// for the service import with the si.to == to, which may not be the key\n\t// for the service import in the map.\n\tfor _, sis := range a.imports.services {\n\t\tfor _, si := range sis {\n\t\t\tif si.acc.Name == destination.Name && si.to == to {\n\t\t\t\tsi.share = allow\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\treturn fmt.Errorf(\"service import not found\")\n}\n\n// AddServiceImport will add a route to an account to send published messages / requests\n// to the destination account. From is the local subject to map, To is the\n// subject that will appear on the destination account. Destination will need","sourceCodeStart":1788,"sourceCodeEnd":1824,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/accounts.go#L1788-L1824","documentation":"setServiceImportSharing refuses to modify service imports on claim-based accounts (server/accounts.go:1806). Accounts whose state is derived from an operator-managed claims JWT must be updated through claim updates, not direct in-memory mutation APIs.","triggerScenarios":"Calling setServiceImportSharing (directly or via AddServiceImport sharing options) on an Account backed by claims, with check=true.","commonSituations":"Using embedded server APIs to mutate an account that was created from a JWT in an operator-mode deployment; scripts that mutate accounts in memory and expect them to persist.","solutions":["Update the account's JWT claims (service import sharing settings) and push the updated claim via the resolver/account update API.","Re-issue the account JWT with the desired exports/imports sharing settings and call UpdateAccountClaims.","If the account is not meant to be claim-based, provision it without JWT backing."],"exampleFix":"// before\nacc.setServiceImportSharing(dest, \"req\", true, true)\n// after\nclaims := jwt.NewAccountClaims(acc.Name)\nclaims.Exports.Add(...)\nsrv.UpdateAccountClaims(oldClaims, claims)","handlingStrategy":"validation","validationCode":"if acc.isClaimAccountLike() {\n    // route through JWT claim update instead\n}","typeGuard":null,"tryCatchPattern":"if err := acc.setServiceImportSharing(dest, to, true, true); err != nil {\n    if strings.Contains(err.Error(), \"claim based accounts\") {\n        // update via UpdateAccountClaims instead\n    }\n}","preventionTips":["Check whether the account is claim-backed before direct mutation","Prefer JWT claim updates in operator mode","Keep account state changes in the claim source of truth"],"tags":["nats-server","jwt-claims","account"],"backgroundTag":"claim-based-account-readonly","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}