Java SDK ApiClient thread safety issues with Knowledge Connections

Fun one today. We’ve got a Spring Boot service using the Genesys Cloud Java SDK v102.1.0 that’s throwing random ConcurrentModificationException errors when hitting the Knowledge API.

The app handles a high volume of connection updates. Why is this happening? It’s likely because the ApiClient isn’t behaving as a thread-safe singleton when multiple threads try to refresh the OAuth token while calling PATCH /api/v2/knowledge/connections/{connectionId}.

The service is basically doing this in a loop:

KnowledgeApi knowledgeApi = new KnowledgeApi(apiClient);
Connection connection = new Connection();
connection.setName("Updated-Connection-Name");
knowledgeApi.updateConnection(connectionId, connection);

Everything works fine for a few hours, then the whole thing just falls apart. We’ve tried wrapping the call in a synchronized block, but that’s killing our throughput and feels like a band-aid. I don’t care about the “correct” architectural pattern right now, I just need a way to stop these crashes without slowing down the entire pipeline.

Would creating a new ApiClient instance per request be faster than dealing with connection pooling issues? It’s probably overkill on the memory side, but it might be the quickest fix to get prod stable.

The SDK’s ApiClient isn’t thread-safe for token refreshes. It’s the same garbage we’ve seen in older community threads where concurrent requests trigger a race condition during the auth handshake.

Stop using a singleton. Instantiate a new client per thread or wrap the refresh logic in a synchronized block to stop the crashes.

2 Likes

Right, so the race condition during the auth handshake is the real culprit here. Why is the client designed to be this fragile? Because consistency is apparently optional. Ffs.

The fix in the earlier reply is spot on, but it’s also worth noting that the token refresh logic is just… ugh. If a thread-safe wrapper isn’t used, the internal state is basically a coin flip when you’re hammering /api/v2/knowledge/connections.

That’s…interesting, honestly. At my last shop we had a similar headache where we were processing high-volume event streams and the SDK’s internal state just couldn’t keep up with the concurrency, which actually reminds me of a bug we tracked as INC-4471 because we were seeing these weird race conditions during token refreshes that messed up our gRPC-Web transformation layer. Since the ApiClient isn’t thread-safe for those auth handshakes, you can’t really rely on a single instance across multiple threads without hitting that ConcurrentModificationException. The fix is usually to move toward a provider pattern or just use a ThreadLocal to ensure each thread has its own client instance, which avoids the collision entirely. If you’re doing high-volume updates to something like /api/v2/knowledge/connections/{connectionId}, you might want to try something like this to keep the clients isolated:

public class ApiClientProvider {
 private static final ThreadLocal<ApiClient> threadLocalClient = ThreadLocal.withInitial(() -> {
 ApiClient client = new ApiClient();
 // Add your auth and region config here
 return client;
 });

 public static ApiClient getClient() {
 return threadLocalClient.get();
 }
}

It’s kind of a pain to manage the lifecycle of those clients, but it’s way better than the random crashes. I remember we spent ages trying to synchronize the refresh logic at my last shop but it just added too much latency to the event processing. Using a ThreadLocal is a bit more blunt but it’s usually the only way to stop the SDK from tripping over itself when the volume spikes. Still wrapping my head around why the SDK handles it this way, but this approach usually clears up those concurrency errors.