[Critical] Verse SpatialMath methods cause ErrRuntime_InfiniteLoop and crash servers

Summary

After 36.00 update my map started crashing sometimes with error logs pointing to Verse native functions, for ex vector3.Length() :

Verse unrecoverable error: ErrRuntime_InfiniteLoop: The runtime terminated prematurely because Verse code was running in an infinite loop. (Computation timed out - see log for stack trace) Truncated callstack follows: SpatialMath:(/Verse.org/SpatialMath:)operator'.Length'(:(/Verse.org/SpatialMath:)vector3,:tuple()) (Unknown source) (Source: Unknown(0,0, 0,0)) (/Verse.org/SpatialMath:)operator'.MakeUnitVector'(:(/Verse.org/SpatialMath:)vector3,:tuple()) (Source: SpatialMath/Vector3.native.verse(99,16, 100,17)) (/beloud-creative@fortnite.com/Pekao?nk/VPets:)GetPetPosAndRotation(:(/Verse.org/SpatialMath:)transform,:(/Verse.org/SpatialMath:)transform) (Source: VPets/pet_math.verse(37,119, 38,120)) InitPlayerAsync(:(/Verse.org/Simulation:)agent,:(/Verse.org/Verse:)event(tuple())) (Source: VPets/pets.verse(73,58, 74,59))

Please select what you are reporting on:

Unreal Editor for Fortnite

What Type of Bug are you experiencing?

Verse

Steps to Reproduce

Please check error logs of map public code: 8754-6289-4338

Expected Result

No crashes when using SpatialMath functions

Observed Result

Rare crashes when using SpatialMath functions

Platform(s)

PC and probably others

Island Code

8754-6289-4338

Hi @Kamyker, could you please send us your log right after you reproduce this? We’re having trouble getting a repro here.

Logs as FortniteGame.log ?

It’s a bit tricky to reproduce as it doesn’t happen very often. Seems to occur more often after player gets a pet (from rebirths) that then follows player all the time making a lot of math calculations.

Yes, if you could, after you get the next repro. Thank you!

This makes no sense, I wouldn’t be able to compile the code and release the map…

Anyway it’s not true, new SpatialMath doesn’t use <decides> anymore, I already tested all edge cases and they don’t crash. It’s completely irrelevant what you wrote there.
image

Old MakeUnitVector from Templorarty/SpatialMath also causes the crash with <decides> context:

Verse unrecoverable error: ErrRuntime_InfiniteLoop: The runtime terminated prematurely because Verse code was running in an infinite loop. (Computation timed out - see log for stack trace) Truncated callstack follows: SpatialMath:SpatialMath_vector3$OverrideFactory (Unknown source) (Source: Unknown(0,0, 0,0)) (/UnrealEngine.com/Temporary/SpatialMath:)operator'.IsFinite'(:(/UnrealEngine.com/Temporary/SpatialMath:)vector3,:tuple()) (Source: SpatialMath/Vector3_Deprecated.native.verse(149,65, 150,66)) (/UnrealEngine.com/Temporary/SpatialMath:)operator'.MakeUnitVector'(:(/UnrealEngine.com/Temporary/SpatialMath:)vector3,:tuple()) (Source: SpatialMath/Vector3_Deprecated.native.verse(83,14, 84,15)) InitPlayerAsync(:(/Verse.org/Simulation:)agent,:(/Verse.org/Verse:)event(tuple())) (Source: VPets/pets.verse(85,49, 86,50))

Other methods from SpatialMath also cause the crash:

FromTransform:

Verse unrecoverable error: ErrRuntime_InfiniteLoop: The runtime terminated prematurely because Verse code was running in an infinite loop. (Computation timed out - see log for stack trace) Truncated callstack follows: SpatialMath:(/UnrealEngine.com/Temporary/SpatialMath:)FromTransform(:(/UnrealEngine.com/Temporary/SpatialMath:)transform) (Unknown source) (Source: Unknown(0,0, 0,0)) InitPlayerAsync(:(/Verse.org/Simulation:)agent,:(/Verse.org/Verse:)event(tuple())) (Source: VPets/pets.verse(72,42, 73,43))

Length():

Verse unrecoverable error: ErrRuntime_InfiniteLoop: The runtime terminated prematurely because Verse code was running in an infinite loop. (Computation timed out - see log for stack trace) Truncated callstack follows: SpatialMath:(/Verse.org/SpatialMath:)operator'.Length'(:(/Verse.org/SpatialMath:)vector3,:tuple()) (Unknown source) (Source: Unknown(0,0, 0,0)) (/Verse.org/SpatialMath:)operator'.MakeUnitVector'(:(/Verse.org/SpatialMath:)vector3,:tuple()) (Source: SpatialMath/Vector3.native.verse(99,16, 100,17)) (/beloud-creative@fortnite.com/Pekao?nk/VPets:)GetPetPosAndRotation(:(/Verse.org/SpatialMath:)transform,:(/Verse.org/SpatialMath:)transform) (Source: VPets/pet_math.verse(37,119, 38,120)) InitPlayerAsync(:(/Verse.org/Simulation:)agent,:(/Verse.org/Verse:)event(tuple())) (Source: VPets/pets.verse(73,58, 74,59))

Adding 2 vectors:

Verse unrecoverable error: ErrRuntime_InfiniteLoop: The runtime terminated prematurely because Verse code was running in an infinite loop. (Computation timed out - see log for stack trace) Truncated callstack follows: SpatialMath:(/Verse.org/SpatialMath:)operator'+'(:(/Verse.org/SpatialMath:)vector3,:(/Verse.org/SpatialMath:)vector3) (Unknown source) (Source: Unknown(0,0, 0,0)) (/beloud-creative@fortnite.com/Pekao?nk/VPets:)GetPetPosAndRotation(:(/Verse.org/SpatialMath:)transform,:(/Verse.org/SpatialMath:)transform) (Source: VPets/pet_math.verse(43,24, 44,25)) InitPlayerAsync(:(/Verse.org/Simulation:)agent,:(/Verse.org/Verse:)event(tuple())) (Source: VPets/pets.verse(73,58, 74,59))

Hi! Reading the relevant bits of the project code I believe this issue has less to do with the SpatialMath module’s functions and more to do with the specific implementation of InitPlayerAsync. In the project specified there is a block of code that is susceptible to infinite loops. It is important to note that loop: does not receive special treatment in a <suspends> context and so anything that would be an infinite loop in a non-<suspends> context would likely also be one in a <suspends> context.

Coroutines can be tricky and, although most of the implementation appears to be reasonable, code leveraging loop: should be treated with extra care. The following is a abstraction providing a safer way to accomplish what the project’s looping code was doing;

# We want to, in perpetuity, move something towards the transform provided by CoolTarget.
MoveSomethingForever(CoolThing:cool_thing, CoolTarget:cool_target)<suspends>:void=
    StepDuration:float := 0.3 #Step duration is defined here so we can use it in multiple places.
    loop:
        if(CoolThing.Representation.IsValid[] && CoolTarget.IsValid[]):
            if (MoveData := ResolveMoveData[ResolveTransform[CoolThing], ResolveTransform[CoolTarget]]):
                #Where cool_thing::PerformMove is <suspends> and will suspend for StepDuration.
                CoolThing.PerformMove(MoveData, StepDuration)
            # It is important to note that, regardless if MoveData had successfully resolved and ran PerformMove, we call Sleep!
            # This is because, if we are unable to resolve MoveData repeatedly in one frame, we could end up in an infinite recursion!
            else:
                Sleep(StepDuration) 
        else:
            Sleep(1.0)

The important thing to note here is that, regardless of the state of CoolThing or CoolTarget and whether MoveData is successfully resolved, there is always a path to a call to Sleep(), ensuring that MoveSomethingForever does not enter into an infinite loop.

If you have any further questions, please let me know.

FORT-933769 has been ‘Closed’. We’re unable to reproduce this issue.

Hi, thank you I see the issue now and will fix my code.

Btw I thought this bug was fixed but it seems that some kind of regression in recent updates made stack traces unavailable. Now this crash shows in dashboard logs as:

Verse unrecoverable error: ErrRuntime_InfiniteLoop: The runtime terminated prematurely because Verse code was running in an infinite loop. (Computation timed out - see log for stack trace) (Callstack unavailable)

Same thing when Err() is used. I remember I had one more InifniteLoop in other place of my code but now I’m unable to find it.

Created another report on this topic [Regression] Verse callstacks unavailable for errors

I also experienced missing stack traces when trying to debug this issue. I’ll make a note of that on FORT-988785.