Skip to content

Android: channel permanently freezes app (stuck on splash) when a message contains U+FFFC alone on a line - IllegalArgumentException: Layout: -2 < 0 #9960

Description

@ryklith

Summary

Opening a channel that contains a message with U+FFFC (OBJECT REPLACEMENT CHARACTER) alone on its own line crashes the React root on Android (IllegalArgumentException: Layout: -2 < 0), leaving the app permanently stuck on the splash screen. iOS and web render the same channel fine.

Environment Information

  • Device Name: Pixel 6 emulator (arm64, Android 16 / API 36, Google APIs image); also reproduced by multiple end users on physical Android devices
  • OS Version: Android 16
  • Mattermost App Version: 2.42.2 (Build 787, GitHub release APK)
  • Mattermost Server Version: 11.9.0 (self-hosted, Team edition)

Steps to reproduce

  1. Post a message whose last line consists only of U+FFFC, e.g. (note the invisible U+FFFC after the blank line):

    some text
    
    
    

    Easiest programmatic repro: POST /api/v4/posts with "message": "some text\n\n".
    Real-world origin: iOS users copy-pasting text together with an inline image puts U+FFFC into the message text.

  2. Open the channel in the Android app.

Notes from bisecting the trigger:

  • U+FFFC inline within a line of text is harmless (renders as the OBJ glyph).
  • U+FFFC alone on its own line crashes 100% of the time.
  • Because the post sits in the channel's recent page, the crash re-triggers on every channel open. Reinstalling the app does not help.

Expected behavior

The message renders (or the character is ignored), like on iOS and web.

Observed behavior (that appears unintentional)

The channel screen never appears; the app falls back to the splash screen and stays there ("frozen image"). End users reported it as the app being stuck/crashed for one specific channel, surviving reinstall and restart.

Logcat shows a native exception during text measurement, which React Native treats as a component error and destroys the React host:

BridgelessReact: ReactHost{0}.handleHostException(message = "Error: Exception in HostFunction: java.lang.IllegalArgumentException: Layout: -2 < 0
	at android.text.Layout.<init>(Layout.java:375)
	at android.text.BoringLayout.<init>(BoringLayout.java:274)
	at android.text.BoringLayout.make(BoringLayout.java:68)
	at com.facebook.react.views.text.TextLayoutManager.createLayout(TextLayoutManager.kt:611)
	at com.facebook.react.views.text.TextLayoutManager.createLayout(TextLayoutManager.kt:821)
	at com.facebook.react.views.text.TextLayoutManager.createLayoutForMeasurement(TextLayoutManager.kt:742)
	at com.facebook.react.views.text.TextLayoutManager.measureText(TextLayoutManager.kt:993)
	at com.facebook.react.fabric.FabricUIManager.measureText(FabricUIManager.java:637)
	...
This error is located at:, stack:
commitMutationEffectsOnFiber@1:379746
...

followed by ReactHost{0}.getOrCreateDestroyTask(): Starting React Native destruction, after which only the splash screen remains.

TurboLogger right before the crash: channel switch to <channel> <id> 1083.0 ms, and the error report has "isComponentError": true.

Possible fixes

The negative width (-2, suspiciously equal to WRAP_CONTENT) reaches android.text.BoringLayout via RN's TextLayoutManager.measureText, so the underlying overflow is arguably a react-native bug. On the Mattermost side, sanitizing U+FFFC in the markdown/text rendering path (e.g. stripping it or replacing standalone occurrences before it reaches a <Text> measure) would avoid the crash entirely and match what users expect, since the character is an artifact of iOS copy-paste rather than intentional content.

Possibly related older reports with matching symptoms but no root cause identified: #7738 (app freezes when opening certain channels), #8234 (error when opening a specific channel).

Server-side workaround that fixed it for our deployment: strip U+FFFC from the affected posts (via PUT /api/v4/posts/{id}/patch so clients resync).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions