Troubleshooting¶
Start with the smallest observable chain:
plugin loaded
→ component initialized
→ local asset prepared
→ On Page Loaded
→ Has Displayed Frame
→ widget has Source Component
→ input routing/focus
→ JavaScript events
Bind On Load Failed before experimenting. Its Error pin is usually more useful than adding an arbitrary delay.
Fast symptom table¶
| Symptom | Likely cause | Fix |
|---|---|---|
| MagicUI does not appear in Plugins | Folder nesting is wrong or MagicUI.uplugin was not copied. |
Verify <Project>/Plugins/MagicUI/MagicUI.uplugin, then restart. |
| “Missing or incompatible modules” | Plugin package does not match UE/platform or is incomplete. | Use the UE 5.8 host-platform package and extract it completely. |
| Canvas is blank | UMG child has no runtime Source Component. | Pass the Actor component into Create Widget and call Set Source Component in Event Construct. |
| On Load Failed says file is missing | Imported asset's linked source moved, or loose path is wrong. | Restore/reimport the link; prefer an imported HTML asset. |
| On Page Loaded fires but texture is null | First render upload has not completed. | Wait until Has Displayed Frame is true before sampling. |
| Editor shows old HTML | Current PIE/view session uses an immutable snapshot. | Stop PIE/destroy views, save source, then start a new session. |
| Packaged game shows no page | Component depends on HTML File Path or uncooked content. | Import HTML, assign HTML Asset, refresh packaged copy, save, and recook. |
| Remote URL fails | Remote navigation is intentionally disabled. | Package content locally as an imported MagicUI Web File. |
| Button does not click | Player input mode/cursor/routing or CSS pointer intent is wrong. | Use Game and UI + cursor shown; test Mouse Routing Always; check pointer-events and cursor CSS. |
| Text input does not type | Widget lacks focus, Keyboard routing is Never, or player is Game Only. | Use Game and UI/UI Only, Automatic or Always keyboard routing, and click/focus the input. |
| Text is soft | Physical render is smaller than displayed region or scaling owners conflict. | Use Auto Resize To Screen + Auto DPI for full screen; inspect Get Render Size. |
| GPU requested, active mode is CPU | Active RHI cannot use accelerated presenter or it fell back. | Use Auto/CPU or test a supported RHI; inspect On Active Render Mode Changed. |
| UI FPS is below Max FPS | Page is static, Unreal/display is capped, or host work cannot sustain it. | Static zero is correct; otherwise inspect effective cap, game FPS, DPI/size, and damage logs. |
Emit Event returns false |
Invalid name/source, page not ready, or component queue is full. | Wait for Page Loaded, validate source/name, and bind On Event Error. |
| Event emit returns true but JS sees nothing | True means async preparation accepted, not delivered; listener may not exist. | Load helper before app JS, register listener early, and watch On Event Error. |
| Gameplay Tag event is ignored | Tag is unregistered or absent from receive container. | Register the tag and select it under Gameplay Tags To Listen For. |
| Material never updates after resize | Texture object was replaced and custom presenter kept old reference. | Re-read Get Rendered Texture after Has Displayed Frame. |
Blank screen diagnosis¶
1. Verify content¶
- The component's HTML Asset is the imported HTML MagicUI Web File.
- Double-click the asset: Status is Exists.
- On Load Failed is bound and does not report a bundle/path error.
- The page uses supported local relative resources.
2. Verify the runtime connection¶
- The correct Actor is placed or spawned.
- Its Magic UI Component reaches Is Initialized.
Create Widgetreceives that same component on your exposed source pin.- Event Construct calls Set Source Component on the actual Magic UI child.
- The returned User Widget is added to the viewport.
3. Verify layout¶
- The Magic UI child is visible and not zero-size.
- In a Canvas Panel it has full-stretch anchors and zero offsets for full screen.
- A parent animation or visibility setting is not hiding/collapsing it.
- Component and widget are not fighting over resize ownership.
4. Verify milestones¶
Print these states at a slow diagnostic interval or in response to an input key:
Is Initialized
Is Page Ready
Has Displayed Frame
Get View Size
Get Render Size
Get Active Render Mode
Avoid printing every Tick in a normal session.
Page and resource failures¶
Source changes do not appear¶
The source snapshot is frozen while a session has live views. Stop PIE, make the edit, save, and start PIE again. Reload alone reloads the frozen current bundle.
CSS/JS/image is missing¶
Check:
- the file sits beneath the imported entry HTML directory;
- its extension is supported;
- each file is at most 1 MiB;
- path case matches exactly;
- no case-only duplicate exists;
- the path uses forward slashes and no traversal; and
- the asset's packaged copy was refreshed before cooking.
Load URL reports policy denied¶
Only internal, prepared, case-sensitive magic://bundle/... URLs are allowed.
There is no Project Settings allowlist for remote origins in this plugin build.
Input diagnosis¶
For a modal test, temporarily set:
Mouse Input Routing = Always
Keyboard Input Routing = Always
Player input mode = Game and UI
Show Mouse Cursor = true
If that works, return to Automatic and fix transparent-region CSS/focus policy.
For a transparent overlay, confirm interactive elements use
pointer-events:auto and cursor:pointer/cursor:text, while decorations use
pointer-events:none.
Remember the known limitation: cached transparent hit state is asynchronous, so the exact first click immediately after pointer entry can race.
Messaging diagnosis¶
Raw bridge¶
- JS sends the value itself with
globalThis.magic.postMessage(value). - Unreal receives compact JSON text through On Page Message.
- Unreal sends a complete valid JSON value string through Post Message To Page.
- JS receives an already parsed value in
event.detail. - send from Unreal only after On Page Loaded.
Named event bridge¶
magicui-events.jsloads before the application script.- the event component points at the correct view.
- simple event names match case exactly.
- Listen For All Events is on, or the exact name is in the allowlist.
- On Event Error is bound.
- Json Payload is a complete JSON value; a string requires quotes.
- the complete UTF-8 envelope fits Max Event Bytes.
See MagicUI Event component for an end-to-end test.
Rendering and performance diagnosis¶
Compare Get Requested Render Mode with Get Active Render Mode. CPU active under Auto is a valid fallback. GPU (Accelerated) is not a GPU browser renderer; it accelerates only the Unreal presentation conversion.
Turn on bounded damage logging temporarily:
Search the Output Log for MAGICUI_DAMAGE_PRESENTED. Each line includes the
physical size, changed rectangle, patch bytes, full-frame equivalent, opacity,
and GPU conversion flag. Turn it back off:
The widget's optional UI FPS counter measures completed MagicUI presentations. A static page returning to 0 FPS is expected and efficient.
Logs and failure events¶
Open:
Search for LogMagicUI, On Load Failed Error text, event errors, and runtime
crash details. Useful delegates are:
- On Load Failed
- On Event Error
- On JavaScript Result with Success=false and Exception
- On Active Render Mode Changed
- On Runtime Process Crashed
Restart after native plugin changes¶
MagicUI's native runtime and owner thread are process-lifetime, and dynamic module reload is unsupported. After replacing/updating the plugin package, close Unreal Editor completely, verify the files, and start it again. A hot reload is not a valid test of replaced native libraries.
If a current package still fails before any component initializes, record the exact Unreal version, platform, plugin package identity, complete LogMagicUI error, and the smallest reproduction graph before requesting support.