Skip to content

StytchClient holds OkHttpClient and CoroutineScope but has no close() to release them #148

Description

@hudson155

Summary

com.stytch.java.consumer.StytchClient (and B2BClient) holds resources that need explicit cleanup but exposes no way to release them. Applications that hold a single StytchClient for the entire process lifetime are fine — anything that creates a StytchClient per tenant, per test, or per config reload leaks until the JVM exits.

What's held

From the bytecode of StytchClient in sdk-11.0.0:

private final kotlinx.coroutines.CoroutineScope coroutineScope;
private final com.stytch.java.http.HttpClient httpClient;
private final com.stytch.java.http.HttpClient fraudHttpClient;

Each HttpClient wraps an okhttp3.OkHttpClient, which owns a Dispatcher backed by an ExecutorService plus a ConnectionPool with idle-eviction threads. The coroutineScope is created from a SupervisorJob and never cancelled.

Because StytchClient doesn't implement Closeable / AutoCloseable and none of the internal fields are exposed, callers can't release any of it.

Why it matters

  • Tests. A test that constructs a real StytchClient (rather than a mock) leaks an OkHttpClient per test method. Across a suite the dispatcher threads pile up until OkHttp's idle eviction runs.
  • Per-tenant clients. Multi-tenant servers that scope a StytchClient per tenant keep every retired tenant's clients alive.
  • Graceful shutdown. Frameworks with structured shutdown can't release the SDK's resources before the rest of the application has stopped, which is awkward when ordering matters.

Proposed fix

StytchClient (and B2BClient) implements AutoCloseable:

class StytchClient(...) : AutoCloseable {
  override fun close() {
    httpClient.client.dispatcher.executorService.shutdown()
    httpClient.client.connectionPool.evictAll()
    fraudHttpClient.client.dispatcher.executorService.shutdown()
    fraudHttpClient.client.connectionPool.evictAll()
    coroutineScope.cancel()
  }
}

Callers can then use StytchClient(...).use { ... } for short-lived cases, and DI containers can register a shutdown hook that calls close().

Environment

  • SDK: com.stytch.java:sdk:11.0.0
  • JVM: 21
  • Kotlin: 2.x

Happy to open a PR if a maintainer signals which direction is preferred (full AutoCloseable versus an explicit shutdown() method).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions