Skip to content

load_level asserts in TickTaskManager and kills the editor (UE 5.8) #66

Description

@harness-lab

Bug: level.load_level asserts in TickTaskManager and kills the editor

Unlike #65 this is not malformed input — it is a plain, documented call with a valid map path.

Environment

  • Engine: UE 5.8.0, CL 55116800, ++UE5+Release-5.8
  • Plugin: UnrealMCPython 2.2.0
  • OS: Windows 11

Repro

With the editor open on some other map, call:

les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
les.load_level('/Game/<YourProject>/Maps/<SomeMap>')

…via util.execute_python (or the equivalent level.load_level action). The client sees
ConnectionResetError [WinError 10054] and the editor process is gone.

Crash

Assertion failed: !LevelList.Contains(TickTaskLevel)
[File: .../Engine/Source/Runtime/Engine/Private/TickTaskManager.cpp] [Line: 1992]

Analysis

The level load is being driven from inside the TCP server's game-thread dispatch, mid-frame,
while the previous level's tick task registration is still live — so the new level's
TickTaskLevel is added while the old one is still in LevelList and the assert fires.

This looks related to a broader pattern in the same dispatch path. Across a day of driving
this plugin I collected 18 crash reports, and every one has UnrealMCPython or
PythonScriptPlugin frames in the stack. Seven of them are:

Ensure condition failed: IsInGameThread()
[File: .../Runtime/Core/Private/Misc/AppTime.cpp] [Line: 38]
Attempted to retrieve FAppTime on a thread where there is no inherited time context.

Those are non-fatal, but they say plainly that engine state is being touched off the game
thread (or on a task with no inherited context). The load_level assert looks like the same
root cause reaching something that is checked rather than ensured.

Workaround

Do not load levels through the bridge. Launch the editor with the map on the command line
instead, which is reliable:

UnrealEditor.exe <Project>.uproject /Game/<Project>/Maps/<SomeMap>

Suggested fix

Defer level loading to a safe point in the frame (next tick / FCoreDelegates post-tick)
rather than executing it inline in the socket dispatch, and generally ensure the dispatch path
runs with an inherited time context so the IsInGameThread ensures stop firing.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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