+
Skip to content

NPE during loading user groups with concurrent deletion #40368

Closed
@lrozenblyum

Description

@lrozenblyum

Before reporting an issue

  • I have read and understood the above terms for submitting issues, and I understand that my issue may be closed without action if I do not follow them.

Area

storage

Describe the bug

During fetching user groups we may face

2025-06-10 11:14:57,477 ERROR [org.keycloak.services.error.KeycloakErrorHandler] (executor-thread-472) Uncaught server error: java.lang.NullPointerException: Cannot invoke "org.keycloak.mod
els.GroupModel.getSubGroupsCount()" because the return value of "org.keycloak.models.cache.infinispan.GroupAdapter.getGroupModel()" is null
        at org.keycloak.models.cache.infinispan.GroupAdapter.getSubGroupsCount(GroupAdapter.java:262)
        at org.keycloak.utils.GroupUtils.populateSubGroupCount(GroupUtils.java:82)
        at org.keycloak.utils.GroupUtils.lambda$populateGroupHierarchyFromSubGroups$2(GroupUtils.java:39)
        at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
        at java.base/java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:395)
        at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:261)
        at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:261)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:510)
        at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
        at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
        at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
        at org.keycloak.utils.ClosingStream.forEach(ClosingStream.java:128)
        at org.keycloak.utils.GroupUtils.populateGroupHierarchyFromSubGroups(GroupUtils.java:31)
        at org.keycloak.services.resources.admin.GroupsResource.getGroups(GroupsResource.java:111)
        at org.keycloak.services.resources.admin.GroupsResource$quarkusrestinvoker$getGroups_09ec752a8eec558a48a704f25fc7f0c07e766424.invoke(Unknown Source)
        at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
        at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
        at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
        at io.quarkus.vertx.core.runtime.VertxCoreRecorder$15.runWith(VertxCoreRecorder.java:638)
        at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2675)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2654)
        at org.jboss.threads.EnhancedQueueExecutor.runThreadBody(EnhancedQueueExecutor.java:1627)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1594)
        at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:11)
        at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:11)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:1583)

(after next occurrences it is shrinked to
2025-06-10 11:38:07,175 ERROR [org.keycloak.services.error.KeycloakErrorHandler] (executor-thread-521) Uncaught server error: java.lang.NullPointerException
(without stack)

Version

26.2.5

Regression

  • The issue is a regression

Expected behavior

No NPE during loading of user groups

Actual behavior

NPE is reproduced

How to Reproduce?


@Test
void createMultiDeleteMultiReadMulti() {
	// create multiple groups
	List<String> groupUuuids = new ArrayList<>();
	IntStream.range(0, 100).forEach(groupIndex -> {
		GroupRepresentation group = new GroupRepresentation();
		group.setName("Test Group " + groupIndex);
		try (Response response = realmResource.groups().add(group)) {
			boolean created = response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL;
			if (created) {
				final String createdResourceLocation = response.getHeaderString("Location");
				String groupUuid = StringUtils.substringAfterLast(createdResourceLocation, "/");
				groupUuuids.add(groupUuid);
			} else {
				fail("Failed to create group: " + response.getStatusInfo().getReasonPhrase());
			}
		}
	});

	AtomicBoolean deletedAll = new AtomicBoolean(false);
	List<Exception> caughtExceptions = new CopyOnWriteArrayList<>();
	// read groups in a separate thread
	new Thread(() -> {
		while (!deletedAll.get()) {
			try {
				// just loading briefs
				realmResource.groups().groups(null, 0, Integer.MAX_VALUE, true);
			} catch (Exception e) {
				caughtExceptions.add(e);
			}
		}
	}).start();

	// delete groups
	groupUuuids.forEach(groupUuid -> {
		realmResource.groups().group(groupUuid).remove();
	});
	deletedAll.set(true);

	assertThat(caughtExceptions, Matchers.empty());
}

Anything else?

  1. The test is green on 22.0.5, No NPE
  2. No sub-groups are created by us, just top-level groups
  3. No user federation enabled, the groups are pure Keycloak internal groups

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

    点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载