Editor hangs
What to do when MifBridge stops answering but the editor is still running: modal windows, a write that timed out, a play session that asks a question, and why a background editor is slow. Each entry says what you see, why, and what to do.
The bridge answers from the editor's main loop
Every call runs on the editor's game thread, inside the same loop that draws the editor. That is what makes calls safe against the engine's own tick, and it has two consequences:
- A call takes about one tick to start. A focused editor ticks quickly, and a background editor ticks slowly (see below).
- Anything that opens a modal window and waits for a person stops that loop. The editor shows a dialog, and the bridge cannot read the socket until it is closed.
A modal window is open
What you see: calls time out or answer modalActive:true, and the editor's window shows a dialog, or a dialog sits behind other windows.
What the bridge does: a modal dialog runs its own loop, and the bridge keeps its server ticking from inside that loop. While a dialog is up, every call is refused at once with:
modalActive: trueeditorState: "modal"retryable: true- a
modalDialogblock withtitle,texts,buttonsandpolicy
Only a short allow-list of calls runs during a dialog: get_modal_dialog, answer_modal_dialog, arm_modal_answer, describe_endpoint and request_status. A call that edits the world is never allowed inside an engine routine that is waiting for its dialog.
What to do:
- Read the dialog with
get_modal_dialog. Do not retry the call that triggered it. - Answer it with
answer_modal_dialog, which clicks one of the dialog's own buttons, or have a person click it. - For a dialog you expect again,
arm_modal_answerrecords a rule that answers a dialog by title or text (answer, defer or ask). open_modal_dialoggoes the other way: it asks the person at the editor a question and records the answer.
answer_modal_dialog and arm_modal_answer are among the calls the default write mode refuses. See safety-and-write-modes.
Operating system dialogs
A file picker or the engine's native message box (a Windows dialog) runs its own message loop and never enters the editor's modal loop. The bridge notices one when the editor has stopped ticking for a moment and an enabled dialog window of the editor process is on screen. It then reads the dialog's text and buttons the same way, and the answer carries source: "native". Pressing a button sends the click message the button would receive from a mouse. close sends a close request: a message box answers its default, a file picker cancels, and a system box with no Cancel ignores it.
What this cannot cover
- A prompt that appears before the editor has started its interface (the bridge is not up yet).
- A window that opens inside a bridge call and is cancelled by the call's own unattended guard. See the next entry.
Why most dialogs never appear
Every call runs with the engine's "unattended script" flag on. The engine's message dialogs then do not show. They return their default answer, which is No for a Yes/No question. A destructive prompt is therefore declined rather than waited on.
What this means for you: a refusal can come from a dialog you never saw. A "no dialog" argument on an engine call usually suppresses the file picker but not the validation, so a duplicate, rename or delete can still ask a question (an invalid name, a clash with an existing destination, an asset editor that still has the asset open) and is then answered No. Endpoints check the predictable cases first and fail with a real reason. Read the reason in the answer.
A second kind of dialog, a warning with a "do not show this again" option, is cancelled by the same flag. The operation is then refused rather than hung.
Progress dialogs are the exception. A slow-task window with a progress bar is deliberately still shown. It stalls the bridge for as long as the task runs rather than forever. add_sublevel and save_dirty_packages can do this. Wait for them.
A write timed out: it may have run
What you see: a read timeout on a call that writes (a spawn, an import, a node add, a compile).
Why: a timeout cannot tell a call that never started from one whose reply was late. A compile, a cook or a long import holds the reply while the work completes, and the work completes.
What the bridge does: each call from the MCP server carries an X-Mif-Request-Id, and the editor keeps each id with its result for 600 seconds. A timed-out call that may write answers outcomeUnknown:true, retryable:false, with its requestId, bucket and checkWith (the request_status call).
What to do:
- Ask
request_statusfor that id, or read the target to see whether the write landed. - Or repeat the identical call. It carries the same id, so the editor returns the first run's result (
replayed:true) or reportsrequestRunning, and nothing runs twice. - Do not answer it with a changed call. That is a new call, gets no protection and can run twice.
Reads are not all "safe to repeat". The read-only group includes saves, play sessions, the console and MetaHuman assembly. The wrapper treats those as writes when they are on the write gate's list.
start_pie refuses where the engine would ask
When a play session starts, the engine checks every loaded Blueprint. Two outcomes open a dialog:
- A Blueprint edited and not compiled, with Auto Recompile Blueprints turned off in the editor's play settings, gets a Yes/No question about compiling. With it on, the default, the engine compiles there.
- A Blueprint with compile errors gets a "play anyway?" dialog. Answering it clears a flag on each Blueprint, so it asks once per error rather than once per session.
start_pie runs the engine's own check first and refuses with blueprintIssues (each problem, the first node errors, and inLevel) and does not start the session. allowErrors:true does what the dialog's "play anyway" does, after compiling the stale ones itself. A deleted Blueprint still in memory never refuses, because nobody can open it to fix it. It is listed as deletedStillInMemory. A session's own warnings and errors come back as pieSession from pie_status and stop_pie.
Play sessions are refused in the default write mode. See safety-and-write-modes.
new_level and load_level while a map has unsaved edits
Both refuse and change nothing while list_dirty_packages with kind:"world" names anything, because the engine would otherwise either save the edits unasked or drop them without asking, and a "save your changes?" dialog would freeze the editor and the bridge. Details are on mistakes-losing-work.
The editor is slow in the background
What you see: the same suite runs three or more times slower when the editor sits behind another window than when it has focus.
Why: a background editor caps its whole engine loop at 3 frames per second, about 333 ms per tick, when "Use Less CPU when in Background" is on (the default) and the editor is not the foreground window, or when every editor window is minimized. A call can land anywhere in that period.
| Editor window | Median per call |
|---|---|
| Restored, behind other windows, bridge driving | 8.5 ms |
| Minimized | 333.4 ms |
What the bridge does: while it is being driven, it tells the editor not to throttle, for five seconds after each authenticated call. No user setting changes, and the editor throttles again when calls stop. The first call after a pause can still wait up to one throttled tick.
What to do: do not compare timings between editors without checking whether each had focus, and do not read the first call after a pause as a slowdown.
Two engine stalls that look like a hang
- Cancelling a clip's compression can deadlock on UE 5.6 and 5.7.
UAnimSequence::TryCancelAsyncTasksholds the clip's compressed data lock while it waits for a task that wants the same lock. There is no CPU use, no log line, and the bridge does not answer.SetSkeletonandWaitOnExistingCompression(false)both go through it. An idle process with no CPU use is a deadlock, not a dialog. - An ensure on an install without debug symbols can stall the game thread for about a minute (64 to 67 seconds measured on 5.6.1), once per call site in a session. A call that times out once and passes on the retry may have met one. Look for "Handled ensure" in the editor log.
Closing a stuck editor
A plain close request makes the editor raise its own save dialog on the person's screen, and the bridge then answers modalActive to whatever call comes next. If a person has the editor open, leave it alone.
Where to go next
- mistakes-losing-work for what
ok:falsedoes not promise - troubleshooting for connection problems
- safety-and-write-modes for the calls the default mode refuses