Before reporting an issue
Area
organizations
Describe the bug
When the experimental stateless feature is enabled, InfinispanUserCacheProviderFactory.isSupported() returns false, so no user cache provider is registered and UserCacheSession is null inside InfinispanOrganizationProvider.
The org-groups membership invalidation path introduced in 26.7 (InfinispanOrganizationProvider.registerOrgGroupsMembershipInvalidation) dereferences this.userCache without a null guard, similar to the class of NPEs fixed by #34603 for the disabled-user-cache case.
As a result, any organization member add that fires GroupModel.GroupMemberJoinEvent throws an NPE. In our case this broke invitation-driven self-registration: the registration form submit returns HTTP 400 and — because the NPE happens mid-flow after the user entity and membership were persisted — leaves partially committed state behind (a disabled user + org membership + the invitation still PENDING), which requires manual cleanup before the user can retry.
KC-SERVICES0013: Failed authentication: java.lang.NullPointerException: Cannot invoke "org.keycloak.models.cache.infinispan.UserCacheSession.registerInvalidation(String)" because "this.userCache" is null
at org.keycloak.models.cache.infinispan.organization.InfinispanOrganizationProvider.registerOrgGroupsMembershipInvalidation(InfinispanOrganizationProvider.java:523)
at org.keycloak.models.cache.infinispan.organization.InfinispanOrganizationProviderFactory.registerOrgGroupMembershipInvalidation(InfinispanOrganizationProviderFactory.java:114)
at org.keycloak.models.cache.infinispan.organization.InfinispanOrganizationProviderFactory.lambda$postInit$2(InfinispanOrganizationProviderFactory.java:69)
at org.keycloak.services.DefaultKeycloakSessionFactory.publish(DefaultKeycloakSessionFactory.java:87)
at org.keycloak.models.GroupModel$GroupMemberJoinEvent.fire(GroupModel.java:114)
at org.keycloak.models.jpa.UserAdapter.joinGroupImpl(UserAdapter.java:490)
at org.keycloak.models.jpa.UserAdapter.joinGroup(UserAdapter.java:472)
at org.keycloak.organization.jpa.JpaOrganizationProvider.addMember(JpaOrganizationProvider.java:221)
at org.keycloak.organization.jpa.JpaOrganizationProvider.addManagedMember(JpaOrganizationProvider.java:190)
at org.keycloak.models.cache.infinispan.organization.InfinispanOrganizationProvider.addManagedMember(InfinispanOrganizationProvider.java:187)
at org.keycloak.authentication.forms.RegistrationUserCreation.addOrganizationMember(RegistrationUserCreation.java:363)
at org.keycloak.authentication.forms.RegistrationUserCreation.success(RegistrationUserCreation.java:158)
Version
26.7.0
Regression
Expected behavior
Organization membership operations (including invitation-driven registration) work when stateless is enabled, or the org-groups invalidation code null-guards the user cache the same way other cache-aware code paths do when the user cache provider is absent.
Actual behavior
NPE as above; the registering user sees a generic error page (HTTP 400), and the realm is left with a disabled user, an org membership row, and the invitation still PENDING.
How to reproduce
- Start Keycloak 26.7.0 with
--features=stateless (plus defaults; organizations enabled on the realm).
- Create an organization in a realm with self-registration enabled.
- Invite a new (not-yet-existing) user via
POST /admin/realms/{realm}/organizations/{id}/members/invite-user.
- Follow the invitation link and submit the registration form.
- Registration fails with the NPE above; the user is left disabled with a persisted org membership and a PENDING invitation.
Anything else?
Workaround: remove stateless from the enabled features — registration works immediately afterwards (after manually deleting the partially-created user).
Root cause chain as we traced it: InfinispanUserCacheProviderFactory.isSupported() returns false under Feature.STATELESS → user cache provider absent → InfinispanOrganizationProvider.userCache is null → unguarded dereference at line 523.
Before reporting an issue
Area
organizations
Describe the bug
When the experimental
statelessfeature is enabled,InfinispanUserCacheProviderFactory.isSupported()returnsfalse, so no user cache provider is registered andUserCacheSessionisnullinsideInfinispanOrganizationProvider.The org-groups membership invalidation path introduced in 26.7 (
InfinispanOrganizationProvider.registerOrgGroupsMembershipInvalidation) dereferencesthis.userCachewithout a null guard, similar to the class of NPEs fixed by #34603 for the disabled-user-cache case.As a result, any organization member add that fires
GroupModel.GroupMemberJoinEventthrows an NPE. In our case this broke invitation-driven self-registration: the registration form submit returns HTTP 400 and — because the NPE happens mid-flow after the user entity and membership were persisted — leaves partially committed state behind (a disabled user + org membership + the invitation still PENDING), which requires manual cleanup before the user can retry.Version
26.7.0
Regression
statelessdisabled; the failing invalidation path is new in 26.7)Expected behavior
Organization membership operations (including invitation-driven registration) work when
statelessis enabled, or the org-groups invalidation code null-guards the user cache the same way other cache-aware code paths do when the user cache provider is absent.Actual behavior
NPE as above; the registering user sees a generic error page (HTTP 400), and the realm is left with a disabled user, an org membership row, and the invitation still PENDING.
How to reproduce
--features=stateless(plus defaults; organizations enabled on the realm).POST /admin/realms/{realm}/organizations/{id}/members/invite-user.Anything else?
Workaround: remove
statelessfrom the enabled features — registration works immediately afterwards (after manually deleting the partially-created user).Root cause chain as we traced it:
InfinispanUserCacheProviderFactory.isSupported()returnsfalseunderFeature.STATELESS→ user cache provider absent →InfinispanOrganizationProvider.userCacheisnull→ unguarded dereference at line 523.