Describe the bug
ComposablePreviewScanner crashes when it encounters a @Preview whose @PreviewParameter provider supplies a Kotlin value class (e.g. PreviewParameterProvider<Dp>). It fails in two places:
getPreviews() — while building the preview's display name (ProvideComposablePreview.methodParametersType)
ComposablePreview.invoke() — while invoking the composable
Both resolve the preview Method through kotlin-reflect (Method.kotlinFunction / ValueClassAwareCaller). For a value-class signature, ValueClassAwareCaller.checkParametersSize asserts that the Kotlin-descriptor arity equals the JVM method arity and throws:
kotlin.reflect.jvm.internal.KotlinReflectionInternalError:
Inconsistent number of parameters in the descriptor and Java reflection object: 1 != 3
The mismatch exists because the Compose compiler appends synthetic parameters (Composer, an Int changed mask, and — with defaults — an Int default mask) that are not in the Kotlin descriptor. kotlin-reflect only routes value-class callables through ValueClassAwareCaller, which is why non-value-class previews are unaffected — they never hit the arity assertion.
To Reproduce
- Define a value-class preview parameter:
class DpValueClassProvider : PreviewParameterProvider<Dp> {
override val values = sequenceOf(32.dp, 64.dp)
}
@Preview
@Composable
fun ValueClassPreviewParameterPreview(
@PreviewParameter(provider = DpValueClassProvider::class) size: Dp,
) { Text(size.toString()) }
- Scan it:
AndroidComposablePreviewScanner().scanPackageTrees("…").getPreviews()
KotlinReflectionInternalError: Inconsistent number of parameters … 1 != 3 is thrown (both on getPreviews()/toString() and on invoke()).
Expected behavior
The preview is scanned and invoked/rendered like any other @PreviewParameter preview, unboxing the value-class argument.
Environment (please complete the following information):
- JDK Version: 17
- AGP Version: 8.11.1
- Composable Preview Scanner Version: 0.9.2 (
master)
- Kotlin Version: 2.0.0
- Compose Version: BOM 2026.04.01 (compose-runtime 1.11.0)
Additional context
Root cause is that the enumeration/invocation paths use kotlin-reflect, which enforces a descriptor↔JVM arity consistency it cannot satisfy for Compose-generated methods. androidx.compose.runtime.reflect.ComposableMethod (java.lang.reflect-based, Compose-ABI aware) already models the synthetic params and is the natural replacement. I have a fix + tests and will open a PR referencing this issue.
Describe the bug
ComposablePreviewScannercrashes when it encounters a@Previewwhose@PreviewParameterprovider supplies a Kotlin value class (e.g.PreviewParameterProvider<Dp>). It fails in two places:getPreviews()— while building the preview's display name (ProvideComposablePreview.methodParametersType)ComposablePreview.invoke()— while invoking the composableBoth resolve the preview
Methodthrough kotlin-reflect (Method.kotlinFunction/ValueClassAwareCaller). For a value-class signature,ValueClassAwareCaller.checkParametersSizeasserts that the Kotlin-descriptor arity equals the JVM method arity and throws:The mismatch exists because the Compose compiler appends synthetic parameters (
Composer, anIntchangedmask, and — with defaults — anIntdefault mask) that are not in the Kotlin descriptor. kotlin-reflect only routes value-class callables throughValueClassAwareCaller, which is why non-value-class previews are unaffected — they never hit the arity assertion.To Reproduce
AndroidComposablePreviewScanner().scanPackageTrees("…").getPreviews()KotlinReflectionInternalError: Inconsistent number of parameters … 1 != 3is thrown (both ongetPreviews()/toString()and oninvoke()).Expected behavior
The preview is scanned and invoked/rendered like any other
@PreviewParameterpreview, unboxing the value-class argument.Environment (please complete the following information):
master)Additional context
Root cause is that the enumeration/invocation paths use kotlin-reflect, which enforces a descriptor↔JVM arity consistency it cannot satisfy for Compose-generated methods.
androidx.compose.runtime.reflect.ComposableMethod(java.lang.reflect-based, Compose-ABI aware) already models the synthetic params and is the natural replacement. I have a fix + tests and will open a PR referencing this issue.