Skip to content

IllegalStateException: OffsetMapping returned invalid mapping crash in BasicRichTextEditor during scroll/fling #717

@mianaliasjad

Description

@mianaliasjad

Describe the bug

A fatal crash occurs in BasicRichTextEditor when the user scrolls or flings through a LazyColumn containing rich text editor instances. The crash is an IllegalStateException thrown by Compose's ValidatingOffsetMapping, indicating that the OffsetMapping provided by the library returns an out-of-bounds index.

Two variants of the same root cause have been observed:

IllegalStateException: OffsetMapping.originalToTransformed returned invalid mapping:
168 -> 168 is not in range of transformed text [0, 167]
IllegalStateException: OffsetMapping.transformedToOriginal returned invalid mapping:
463 -> 463 is not in range of original text [0, 460]

In both cases the mapped index is exactly 1 beyond the valid range, suggesting an off-by-one error in the OffsetMapping implementation when the transformed text length differs from the original.


Stack Trace

Fatal Exception: java.lang.IllegalStateException:
OffsetMapping.originalToTransformed returned invalid mapping:
168 -> 168 is not in range of transformed text [0, 167]
  at androidx.compose.foundation.internal.InlineClassHelperKt.throwIllegalStateException
  at androidx.compose.foundation.text.ValidatingOffsetMappingKt.validateOriginalToTransformed
  at androidx.compose.foundation.text.ValidatingOffsetMappingKt.throwIfNotValidTransform
  at androidx.compose.foundation.text.ValidatingOffsetMappingKt.filterWithValidation
  at androidx.compose.foundation.text.TextFieldScrollKt.defaultTextFieldScroll
  at androidx.compose.foundation.text.CoreTextFieldKt.CoreTextField
  at com.mohamedrejeb.richeditor.ui.BasicRichTextEditorKt.BasicRichTextEditor$lambda$9$0 (BasicRichTextEditor.kt:254)
  at androidx.compose.foundation.lazy.LazyListMeasureKt.measureLazyList
  at androidx.compose.foundation.lazy.LazyListState.onScroll$foundation

To Reproduce

  1. Place one or more BasicRichTextEditor instances inside a LazyColumn
  2. Populate the editors with rich text content
  3. Scroll or fling through the list rapidly
  4. Crash occurs

Root Cause Analysis

The stack trace confirms the crash originates from a LazyColumn scroll/fling:

  • LazyListState.onScroll triggers a remeasure of lazy list items
  • This causes recomposition of BasicRichTextEditor inside a lazy item
  • During recomposition, OffsetMapping.originalToTransformed (or transformedToOriginal) returns an index that is 1 beyond the valid range of the transformed/original text
  • Compose's ValidatingOffsetMapping catches this and throws IllegalStateException

The likely cause is that the OffsetMapping implementation does not clamp the returned index when the transformed text length differs from the original (e.g. due to applied spans or annotations). A possible fix:

return offset.coerceIn(0, transformedText.length)

Expected Behavior

OffsetMapping.originalToTransformed and transformedToOriginal should always return indices within the valid bounds of the respective text, even when transformed and original text lengths differ.


Environment

  • Library version: 1.0.0-rc14, although it never appeared in version 1.0.0-rc13
  • CMP version: 1.10.3
  • Android API level: Android 14
  • Device/emulator: Multiple brands

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions