Crash in FTextRange::CalculateLineRangesFromString()

We are having this strange occasional crash in slate. We have a chat widget in a networked game built in UMG.
Somewhat frequently testers get a crash.

It turns out that this is happening from a call chain that calls FTextBlockLayout::ComputeDesiredSize() and the FText& returned from line 41 in TextBlockLayout.cpp:

const FText& TextToSet = InWidgetArgs.Text.Get(FText::GetEmpty());

TextToSet.ToString() returns a null reference (if you do a check(&TextToSet.ToString() != nullptr) it will assert). This is passed into UpdateTextLayout and eventually crashes inside FTextRange.

Digging in it looks like the DisplayString of the TextToSet is null (the TSharedRef is empty).

Here is the full callstack. I’ve hacked around it be testing for this case but I’d really like to know how this is happening at all! My only very vague guess is that something is bungled during replication since the text is transmitted from client->server->client to reflect the chat messages around to all the players and that somehow the text is bad after replication. The string concatenation is all done in blueprints.

Deadhold.exe!FTextRange::CalculateLineRangesFromString() (0x000000000199193d) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slate\private\framework\text\textrange.cpp:11]
Deadhold.exe!FPlainTextLayoutMarshaller::SetText() (0x00000000019d4987) + 5 bytes [c:\deadhold\unrealengine\engine\source\runtime\slate\private\framework\text\plaintextlayoutmarshaller.cpp:23]
Deadhold.exe!FTextBlockLayout::UpdateTextLayout() (0x0000000001a99813) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slate\private\widgets\text\textblocklayout.cpp:130]
Deadhold.exe!FTextBlockLayout::ComputeDesiredSize() (0x0000000001a7ea24) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slate\private\widgets\text\textblocklayout.cpp:63]
Deadhold.exe!STextBlock::ComputeDesiredSize() (0x0000000001a7f059) + 143 bytes [c:\deadhold\unrealengine\engine\source\runtime\slate\private\widgets\text\stextblock.cpp:189]
Deadhold.exe!SWidget::CacheDesiredSize() (0x0000000001846bc5) + 15 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:370]
Deadhold.exe!SWidget::SlatePrepass() (0x000000000188b232) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:360]
Deadhold.exe!SWidget::SlatePrepass() (0x000000000188b232) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:360]
Deadhold.exe!SWidget::SlatePrepass() (0x000000000188b232) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:360]
Deadhold.exe!SWidget::SlatePrepass() (0x000000000188b232) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:360]
Deadhold.exe!SWidget::SlatePrepass() (0x000000000188b232) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:360]
Deadhold.exe!SWidget::SlatePrepass() (0x000000000188b232) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:360]
Deadhold.exe!SWidget::SlatePrepass() (0x000000000188b232) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:360]
Deadhold.exe!SWidget::SlatePrepass() (0x000000000188b232) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:360]
Deadhold.exe!SWidget::SlatePrepass() (0x000000000188b232) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:360]
Deadhold.exe!SWidget::SlatePrepass() (0x000000000188b232) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:360]
Deadhold.exe!SWidget::SlatePrepass() (0x000000000188b232) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:360]
Deadhold.exe!SWidget::SlatePrepass() (0x000000000188b232) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:360]
Deadhold.exe!SWidget::SlatePrepass() (0x000000000188b232) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:360]
Deadhold.exe!SWidget::SlatePrepass() (0x000000000188b232) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:360]
Deadhold.exe!SWidget::SlatePrepass() (0x000000000188b232) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:360]
Deadhold.exe!SWidget::SlatePrepass() (0x000000000188b232) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:360]
Deadhold.exe!SWidget::SlatePrepass() (0x000000000188b232) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:360]
Deadhold.exe!SWidget::SlatePrepass() (0x000000000188b232) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:360]
Deadhold.exe!SWidget::SlatePrepass() (0x000000000188b232) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:360]
Deadhold.exe!SWidget::SlatePrepass() (0x000000000188b232) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slatecore\private\widgets\swidget.cpp:360]
Deadhold.exe!FSlateApplication::TickWindowAndChildren() (0x000000000193ce91) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slate\private\framework\application\slateapplication.cpp:852]
Deadhold.exe!FSlateApplication::Tick() (0x000000000193cbea) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\slate\private\framework\application\slateapplication.cpp:1250]
Deadhold.exe!FEngineLoop::Tick() (0x000000000136f4b5) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\launch\private\launchengineloop.cpp:2239]
Deadhold.exe!GuardedMain() (0x000000000136565a) + 0 bytes [c:\deadhold\unrealengine\engine\source\runtime\launch\private\launch.cpp:131]
Deadhold.exe!GuardedMainWrapper() (0x0000000001365738) + 17 bytes [c:\deadhold\unrealengine\engine\source\runtime\launch\private\windows\launchwindows.cpp:137]
Deadhold.exe!WinMain() (0x0000000001370996) + 13 bytes [c:\deadhold\unrealengine\engine\source\runtime\launch\private\windows\launchwindows.cpp:202]
Deadhold.exe!__tmainCRTStartup() (0x000000000390a1a0) + 14 bytes [f:\dd\vctools\crt\crtw32\dllstuff\crtexe.c:618]
KERNEL32.DLL!UnknownFunction (0x0000000076be8624) + 0 bytes [UnknownFile:0]
ntdll.dll!UnknownFunction (0x00000000776bbf39) + 0 bytes [UnknownFile:0]
ntdll.dll!UnknownFunction (0x00000000776bbf0c) + 0 bytes [UnknownFile:0]
ntdll.dll!UnknownFunction (0x00000000776bbf0c) + 0 bytes [UnknownFile:0]

I’ve seen a few different ways to get this sort of crash recently.

There was one issue with file-scope statics in a monolithic build being initialised before the empty FText instance had been set up, leading to it having a null TSharedRef. This was fixed by b912f53729b908b031e4f94895f5d28b6edaf91a (and is present in 4.7).

I’ve also seen some issues with people creating arrays of FText and then somehow zeroing the FText instances in the array (eg, by calling SetNumZeroed).

FText serialisation over the network is done via a string, so the reconstructed FText on the other side should always have a valid string pointer, even if that string is empty.

What does your function which binds the text to your chat widget look like, and which version of UE4 are you using?

Thanks Jamie,

I’m not sure it’s the chat log itself, but this is some good information… For chat the APlayerState and an FText are in a BP struct that is replicated to all clients using a multicast BP event. When a new message is received a UMG scroll box widget is cleared and then new widgets are inserted into the box (which are just two Text widgets in UMG).

This could maybe be from other widgets we have on screen. I’m not sure. Since this is all done with BP I don’t think there are any calls to SetNumZeroed anywhere.

Did you get any closer to tracking this down?

No unfortunately not, I’m just testing for null and avoiding the case for now. I’ve no idea what the issue is, all of our UI is done via UMG. I’m not actually 100% sure it’s the chat window, it could be any number of other text fields. All of the UI is assembled in BP as well and I didn’t see anything wrong when I looked.