I see that users are getting the following error in my app. The problem is that the stacktrace doesn't lead to the point of origin in my app. Although I highly suspect it is a certain custom text field (Code below) that causes the error. Since I cannot reproduce the error on my own, I wonder if someone has some tips on how to investigate such issues in general or tips on this specific crash.
Code causing issues (likely, not certain)
@Composable
fun PINTextField(
value: String,
onValueChange: (String) -> Unit,
pinLength: Int,
allowedCharacters: (Char) -> Boolean,
modifier: Modifier = Modifier,
hint: String? = null,
enabled: Boolean = true,
errorText: String? = null,
boxSize: Dp = 40.dp,
boxSpacing: Dp = 12.dp,
backgroundColor: Color = Theme.colors.surface,
borderColor: Color = Theme.colors.border,
focusedBorderColor: Color = Theme.colors.primary,
errorColor: Color = Theme.colors.error,
textColor: Color = Theme.colors.onBackground,
cornerRadius: Dp = 14.dp,
borderWidth: Dp = 2.dp
) {
var textFieldValue by remember {
mutableStateOf(TextFieldValue(value, TextRange(value.length)))
}
var isFocused by remember { mutableStateOf(false) }
LaunchedEffect(value) {
if (textFieldValue.text != value) {
textFieldValue = TextFieldValue(value.toUpperCase(Locale.current), TextRange(value.length))
}
}
Column(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally
) {
if (hint != null) {
Text(
text = hint,
style = Theme.typography.hint(),
color = Theme.colors.onBackground,
fontWeight = FontWeight.Medium
)
Height(8.dp)
}
BasicTextField(
value = textFieldValue,
onValueChange = { newValue ->
if (newValue.text.length <= pinLength && newValue.text.all(allowedCharacters)) {
textFieldValue = newValue
onValueChange(newValue.text.toUpperCase(Locale.current))
}
},
modifier = Modifier.onFocusChanged { focusState ->
isFocused = focusState.isFocused
},
enabled = enabled,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text),
singleLine = true,
textStyle = TextStyle(color = Color.Transparent), // Hide the actual text
decorationBox = { _ ->
Row(
horizontalArrangement = Arrangement.spacedBy(boxSpacing),
verticalAlignment = Alignment.CenterVertically
) {
repeat(pinLength) { index ->
val currentFieldFocused = textFieldValue.selection.start == index && isFocused
val hasChar = index < value.length
val char = if (hasChar) value[index].toString() else ""
val currentBorderColor = when {
errorText != null -> errorColor
!enabled -> borderColor
currentFieldFocused -> focusedBorderColor
hasChar -> focusedBorderColor
else -> borderColor
}
Box(
modifier = Modifier
.size(boxSize)
.clip(RoundedCornerShape(cornerRadius))
.background(backgroundColor)
.border(
borderWidth,
currentBorderColor,
RoundedCornerShape(cornerRadius)
),
contentAlignment = Alignment.Center
) {
Text(
text = char,
style = Theme.typography.body1(),
color = textColor,
fontSize = 16.sp,
fontWeight = FontWeight.Medium
)
}
}
}
}
)
if (errorText != null) {
Height(8.dp)
Text(
text = errorText,
style = Theme.typography.body2(),
color = Theme.colors.error,
fontSize = 14.sp
)
}
}
}
The crash stacktrace
Fatal Exception: java.lang.IllegalStateException: Required value was null.
at androidx.compose.foundation.internal.InlineClassHelperKt.throwIllegalStateExceptionForNullCheck(InlineClassHelper.kt:30)
at androidx.compose.foundation.internal.InlineClassHelperKt.checkPreconditionNotNull(InlineClassHelper.kt:86)
at androidx.compose.foundation.text.selection.TextFieldSelectionManager._get_contextMenuAreaModifier_$lambda$1(TextFieldSelectionManager.kt:233)
at androidx.compose.foundation.text.contextmenu.modifier.TextContextMenuToolbarHandlerNode.contentBounds(TextContextMenuToolbarHandlerModifier.kt:204)
at androidx.compose.foundation.text.contextmenu.internal.AndroidTextContextMenuToolbarProvider.calculateBoundsInRoot(AndroidTextContextMenuToolbarProvider.android.kt:223)
at androidx.compose.foundation.text.contextmenu.internal.AndroidTextContextMenuToolbarProvider.observeAndGetBounds$lambda$7(AndroidTextContextMenuToolbarProvider.android.kt:219)
at androidx.compose.foundation.text.contextmenu.internal.AndroidTextContextMenuToolbarProvider.observeReadsAndGet$lambda$8(AndroidTextContextMenuToolbarProvider.android.kt:237)
at androidx.compose.runtime.snapshots.Snapshot.enter(Snapshot.java:151)
at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:528)
at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:464)
at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:248)
at androidx.compose.foundation.text.contextmenu.internal.AndroidTextContextMenuToolbarProvider.observeReadsAndGet$lambda$8(AndroidTextContextMenuToolbarProvider.android.kt:237)
at androidx.compose.foundation.text.contextmenu.internal.AndroidTextContextMenuToolbarProvider.observeReadsAndGet(AndroidTextContextMenuToolbarProvider.android.kt:237)
at androidx.compose.foundation.text.contextmenu.internal.AndroidTextContextMenuToolbarProvider.observeAndGetBounds$lambda$7(AndroidTextContextMenuToolbarProvider.android.kt:219)
at androidx.compose.foundation.text.contextmenu.internal.AndroidTextContextMenuToolbarProvider.observeAndGetBounds(AndroidTextContextMenuToolbarProvider.android.kt:219)
at androidx.compose.foundation.text.contextmenu.internal.AndroidTextContextMenuToolbarProvider.createActionModeCallback$lambda$5(AndroidTextContextMenuToolbarProvider.android.kt:209)
at androidx.compose.foundation.text.contextmenu.internal.AndroidTextContextMenuToolbarProvider$TextActionModeCallbackImpl.onGetContentRect(AndroidTextContextMenuToolbarProvider.android.kt:249)
at androidx.compose.foundation.text.contextmenu.internal.FloatingTextActionModeCallback.onGetContentRect(AndroidTextContextMenuToolbarProvider.android.kt:398)
at com.android.internal.policy.DecorView$ActionModeCallback2Wrapper.onGetContentRect(DecorView.java:2966)
at com.android.internal.view.FloatingActionMode.invalidateContentRect(FloatingActionMode.java:154)
at com.android.internal.view.FloatingActionMode.invalidate(FloatingActionMode.java:149)
at com.android.internal.policy.DecorView.setHandledFloatingActionMode(DecorView.java:2181)
at com.android.internal.policy.DecorView.setHandledActionMode(DecorView.java:2021)
at com.android.internal.policy.DecorView.startActionMode(DecorView.java:940)
at com.android.internal.policy.DecorView.startActionModeForChild(DecorView.java:895)
at android.view.ViewGroup.startActionModeForChild(ViewGroup.java:1044)
at android.view.ViewGroup.startActionModeForChild(ViewGroup.java:1044)
at android.view.ViewGroup.startActionModeForChild(ViewGroup.java:1044)
at android.view.View.startActionMode(View.java:7882)
at androidx.compose.foundation.text.contextmenu.internal.TextToolbarHelperApi23.startActionMode(AndroidTextContextMenuToolbarProvider.android.kt:350)
at androidx.compose.foundation.text.contextmenu.internal.TextToolbarHelper.startActionMode(AndroidTextContextMenuToolbarProvider.android.kt:329)
at androidx.compose.foundation.text.contextmenu.internal.AndroidTextContextMenuToolbarProvider$showTextContextMenu$2.invokeSuspend(AndroidTextContextMenuToolbarProvider.android.kt:176)
at androidx.compose.foundation.text.contextmenu.internal.AndroidTextContextMenuToolbarProvider$showTextContextMenu$2.invoke(AndroidTextContextMenuToolbarProvider.android.kt:2)
at androidx.compose.foundation.text.contextmenu.internal.AndroidTextContextMenuToolbarProvider$showTextContextMenu$2.invoke(AndroidTextContextMenuToolbarProvider.android.kt:1)
at androidx.compose.foundation.MutatorMutex$mutate$2.invokeSuspend(MutatorMutex.kt:127)
at androidx.compose.foundation.MutatorMutex$mutate$2.invoke(MutatorMutex.kt:2)
at androidx.compose.foundation.MutatorMutex$mutate$2.invoke(MutatorMutex.kt:1)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndspatched(Undispatched.kt:66)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:43)
at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScope.kt:286)
at androidx.compose.foundation.MutatorMutex.mutate(MutatorMutex.kt:120)
at androidx.compose.foundation.MutatorMutex.mutate$default(MutatorMutex.kt:117)
at androidx.compose.foundation.text.contextmenu.internal.AndroidTextContextMenuToolbarProvider.showTextContextMenu(AndroidTextContextMenuToolbarProvider.android.kt:156)
at androidx.compose.foundation.text.contextmenu.modifier.TextContextMenuToolbarHandlerNode$show$1.invokeSuspend(TextContextMenuToolbarHandlerModifier.kt:183)
at androidx.compose.foundation.text.contextmenu.modifier.TextContextMenuToolbarHandlerNode$show$1.invoke(TextContextMenuToolbarHandlerModifier.kt:2)
at androidx.compose.foundation.text.contextmenu.modifier.TextContextMenuToolbarHandlerNode$show$1.invoke(TextContextMenuToolbarHandlerModifier.kt:1)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startCoroutineUndispatched(Undispatched.kt:20)
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:360)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:134)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:53)
at kotlinx.coroutines.BuildersKt.launch(Builders.kt:1)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:44)
at kotlinx.coroutines.BuildersKt.launch$default(Builders.kt:1)
at androidx.compose.foundation.text.contextmenu.modifier.TextContextMenuToolbarHandlerNode.show(TextContextMenuToolbarHandlerModifier.kt:180)
at androidx.compose.foundation.text.contextmenu.modifier.ToolbarRequesterImpl.show(TextContextMenuToolbarHandlerModifier.kt:68)
at androidx.compose.foundation.text.selection.TextFieldSelectionManager.showSelectionToolbar$foundation_release(TextFieldSelectionManager.kt:959)
at androidx.compose.foundation.text.selection.TextFieldSelectionManager.updateFloatingToolbar(TextFieldSelectionManager.kt:949)
at androidx.compose.foundation.text.selection.TextFieldSelectionManager.access$getDragBeginSelection$p(TextFieldSelectionManager.kt:83)
at androidx.compose.foundation.text.selection.TextFieldSelectionManager.access$updateFloatingToolbar(TextFieldSelectionManager.kt:83)
at androidx.compose.foundation.text.selection.TextFieldSelectionManager$touchSelectionObserver$1.onEnd(TextFieldSelectionManager.kt:398)
at androidx.compose.foundation.text.selection.TextFieldSelectionManager$touchSelectionObserver$1.onStop(TextFieldSelectionManager.kt:391)
at androidx.compose.foundation.text.selection.SelectionGesturesKt.touchSelection(SelectionGestures.kt:137)
at androidx.compose.foundation.text.selection.SelectionGesturesKt.access$touchSelectionSubsequentPress(SelectionGestures.kt:1)
at androidx.compose.foundation.text.selection.SelectionGesturesKt.access$touchSelection(SelectionGestures.kt:1)
at androidx.compose.foundation.text.selection.SelectionGesturesKt$touchSelection$1.invokeSuspend(SelectionGestures.kt:11)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:34)
at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:163)
at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:152)
at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:470)
at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$kotlinx_coroutines_core(CancellableContinuationImpl.kt:504)
at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$kotlinx_coroutines_core$default(CancellableContinuationImpl.kt:493)
at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:359)
at androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNodeImpl$PointerEventHandlerCoroutine.offerPointerEvent(SuspendingPointerInputFilter.kt:829)
at androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNodeImpl.dispatchPointerEvent(SuspendingPointerInputFilter.kt:699)
at androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNodeImpl.onPointerEvent-H0pRuoY(SuspendingPointerInputFilter.kt:725)
at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:436)
at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:422)
at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:422)
at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:422)
at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:422)
at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:422)
at androidx.compose.ui.input.pointer.NodeParent.dispatchMainEventPass(HitPathTracker.kt:275)
at androidx.compose.ui.input.pointer.HitPathTracker.dispatchChanges(HitPathTracker.kt:171)
at androidx.compose.ui.input.pointer.PointerInputEventProcessor.process-BIzXfog(PointerInputEventProcessor.kt:118)
at androidx.compose.ui.platform.AndroidComposeView.sendMotionEvent-8iAsVTc(AndroidComposeView.android.kt:2428)
at androidx.compose.ui.platform.AndroidComposeView.handleMotionEvent-8iAsVTc(AndroidComposeView.android.kt:2378)
at androidx.compose.ui.platform.AndroidComposeView.dispatchTouchEvent(AndroidComposeView.android.kt:2249)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3201)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2844)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3201)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2844)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3201)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2844)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3201)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2844)
at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:510)
at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1968)
at android.app.Activity.dispatchTouchEvent(Activity.java:4414)
at curtains.internal.WindowCallbackWrapper$dispatchTouchEvent$dispatch$1.invoke(WindowCallbackWrapper.kt:58)
at curtains.internal.WindowCallbackWrapper$dispatchTouchEvent$dispatch$1.invoke(WindowCallbackWrapper.kt:52)
at com.posthog.android.replay.PostHogReplayIntegration.onTouchEventListener$lambda$7(PostHogReplayIntegration.kt:240)
at curtains.internal.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.kt:65)
at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:468)
at android.view.View.dispatchPointerEvent(View.java:15985)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:7468)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:7243)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:6653)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:6710)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:6676)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:6841)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:6684)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:6898)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:6657)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:6710)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:6676)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:6684)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:6657)
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:9787)
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:9734)
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:9676)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:9938)
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:272)
at android.os.MessageQueue.nativePollOnce(MessageQueue.java)
at android.os.MessageQueue.next(MessageQueue.java:335)
at android.os.Looper.loopOnce(Looper.java:194)
at android.os.Looper.loop(Looper.java:338)
at android.app.ActivityThread.main(ActivityThread.java:8470)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:600)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1064)