Skip to content

[BUG] Scanning/invoking a @Preview with a value class @PreviewParameter throws KotlinReflectionInternalError #141

Description

@nshurtz

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

  1. 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()) }
  2. Scan it: AndroidComposablePreviewScanner().scanPackageTrees("…").getPreviews()
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions