growthbook / growthbook/growthbook-openfeature-provider-java

IllegalArgumentException due to TypeToken type variable in Growthbook open feature provider sdk during flag evaluation

Open
#1 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
2
Forks
4
PR merge metrics
No merged PRs in 30d

Description

While integrating the Growthbook open feature provider in a Kotlin-based Gradle Spring Boot project, we encountered the following error during flag value evaluation:

`java.lang.IllegalArgumentException: TypeToken type argument must not contain a type variable; captured type variable ValueType declared by class growthbook.sdk.java.FeatureRule
See https://github.com/google/gson/blob/main/Troubleshooting.md#typetoken-type-variable`

Details:

The error originates from the deserialization of attributes in the evaluation context using Gson in the Growthbook open feature provider SDK.

At runtime, Gson does not have sufficient type information due to type erasure of generics, specifically related to the ValueType generic parameter in FeatureRule.

This results in an IllegalArgumentException when Gson attempts to instantiate a TypeToken with a type variable, which is not supported.

Reference from Gson Troubleshooting confirms that TypeToken cannot capture type variables due to Java's type erasure, and suggests using explicit parameterization or alternative approaches for type-safe deserialization.

This issue is not seen when using direct Growthbook SDK.

Growthbook is self hosted in docker.

Target code:

Configuration:

@Configuration
class ConfigOpenFeature {

private lateinit var provider: GrowthBookProvider
private val log: Logger = LoggerFactory.getLogger(ConfigOpenFeature::class.java)

@Bean
fun gbProvider(): GrowthBookProvider {

// mandatory refreshCallback as open feature sdk calls refresh immediately after getting provider
val refreshCallback = object : FeatureRefreshCallback {
override fun onRefresh(featureKey: String) {
log.info("Refreshing $featureKey")
}

override fun onError(throwable: Throwable?) {
log.info("Failed to refresh: $throwable")
}
}
val options =
Options
.builder()
.apiHost("http://localhost:3100")
.clientKey("OUR_CLIENT_KEY")
.featureRefreshCallback(refreshCallback)
.build()

// Create and initialize the provider
provider = GrowthBookProvider(options)
return provider
}

@Bean
@DependsOn("gbProvider")
fun ofClient(provider: GrowthBookProvider): Client? {

// Set the provider in OpenFeature
OpenFeatureAPI.getInstance().setProvider(provider)

// Create a client
return OpenFeatureAPI.getInstance().getClient()
}

fun shutdown(provider: GrowthBookProvider) {
provider.shutdown()
}
}`

Feature evaluation calling code:

val map: Map = mapOf(
"country" to Value("US"),
"user_id" to Value(user.id)
)
val context: EvaluationContext = ImmutableContext(user.id, map)
val response = openFeatureClient.getStringValue("testing-flag", "", context)

Versions used:

build.gradle.kts:

implementation(group = "com.github.growthbook", name = "growthbook-openfeature-provider-java", version = "0.0.2")

implementation("dev.openfeature:sdk:1.12.2")

implementation("com.github.growthbook:growthbook-sdk-java:0.9.92")

kotlin compiler version: 2.1.10

project jvm: Java 21

Impact:

This prevents proper evaluation and deserialization of feature flags in the Growthbook Java SDK when used in Kotlin/Spring Boot environments.

Requested fix:

A library fix is required to handle deserialization correctly without capturing generic type variables in TypeToken.

Possible solutions include refactoring to pass explicit type parameters or utilizing Gson’s TypeToken.getParameterized(...) method to provide concrete type information at runtime.

If additional information or logs are needed, please let me know.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with build.gradle.kts and reproduce the failure using the listed GrowthBook, OpenFeature, Kotlin, and Java versions. Trace the provider's Gson deserialization path involving FeatureRule and the evaluation context. Done means flag evaluation succeeds in the Kotlin/Spring Boot setup without the TypeToken exception and the behavior is covered by a regression check.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, kotlin, spring-boot
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.