You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
OpenClaw 2026.6.10 (commit aa69b12d0086), built 2026-06-24
macOS 26.5.1 (Darwin 25.5.0), Apple Silicon
Gateway: launchd-managed (~/Library/LaunchAgents/ai.openclaw.gateway.plist), mode: local, loopback bind, port 18789
Node 26 (Homebrew), gateway installed at /opt/homebrew/lib/node_modules/openclaw
Summary
When the gateway HTTP server is briefly unreachable (e.g. during a service restart), the Control UI's static index.html loads but the app bundle (./assets/index-*.js, ~1 MB) fails to fetch. The openclaw-app custom element never registers, and after the 12s watchdog fires the page shows the "Control UI did not start" fallback dialog.
Two problems compound here:
The fallback blames the wrong thing. The dialog text says "A browser extension or early content script may be blocking module execution" and advises a clean profile / disabling extensions. In practice the real cause is a transient module-fetch failure while the gateway was mid-restart — no extension involved. This sends users down a wrong debugging path.
The UI cannot self-recover. Once the initial <script type="module"> fails, the only path back is a full page reload ("Try again"). The "Keep waiting" button re-arms the 12s watchdog but never re-attempts the module download, so even after the gateway is healthy again the UI stays stuck on the dialog until the user manually reloads.
Reproduction
Have the gateway managed by launchd with KeepAlive=true + ThrottleInterval=10 (the default plist OpenClaw generates).
Relaunch the OpenClaw macOS app. This triggers the gateway to restart (in my logs, twice per launch — see below), each restart leaving the HTTP server down for ~10s (the launchd throttle interval).
Open / already-have-open the Control UI so its page loads during a down-window.
The static HTML loads, the module fetch fails, and ~12s later the "Control UI did not start" dialog appears.
Click "Keep waiting" → dialog reappears 12s later; it never recovers on its own. Only "Try again" (full reload) fixes it.
Root cause / relevant code
In dist/control-ui/index.html:
armFallbackTimer() sets a 12s setTimeout(showFallback).
customElements.whenDefined("openclaw-app") clears the timer + hides the fallback on success.
If ./assets/index-*.js fails to fetch/execute (gateway down), the element never defines, whenDefined never resolves, and the timer fires → fallback shown.
The "Keep waiting" handler only does hideFallback(); armFallbackTimer(); — it re-arms the watchdog but does not re-request or re-execute the failed module. A <script type="module"> that already failed will not re-run on its own, so this path can never recover.
The "Try again" handler does window.location.reload() — the only working recovery.
Contributing factor on the gateway side: each app launch produces two SIGTERM→respawn cycles, and ThrottleInterval=10 in the generated plist means each respawn waits ~10s, widening the window in which the UI can load into a dead server. Example from /private/tmp/openclaw/openclaw-YYYY-MM-DD.log:
16:16:22 received SIGTERM; shutting down
16:16:40 http server listening (throttle ~10s later)
16:16:41 received SIGTERM; shutting down (~1.3s after coming up)
16:16:51 http server listening (throttle ~10s later)
Expected behavior
The Control UI should retry loading the app bundle with backoff instead of giving up permanently, so it recovers on its own once the gateway is reachable again (this is the common case: the gateway is just restarting).
The fallback message should distinguish "gateway unreachable / restarting" from "module blocked by an extension." A quick health probe (e.g. fetch a small / or /control-ui-config.json and check reachability) would let the dialog say "Gateway is restarting, retrying…" vs. the extension advice.
Ideally the app window shouldn't render the Control UI into a known-restarting gateway, or should wait for the gateway to report ready before loading the bundle.
Suggested fixes (any subset)
Auto-retry the bundle: on module-load failure or watchdog timeout, re-attempt loading ./assets/index-*.js with exponential backoff (cache-busting query) before showing the terminal dialog; make "Keep waiting" actually re-attempt the fetch rather than just re-arm the timer.
Better diagnosis: probe gateway reachability when the watchdog fires and branch the message (restarting vs. blocked).
Gateway side: avoid the double-restart per app launch, and/or expose the launchd ThrottleInterval as a config option (currently only editable via the auto-generated plist, which OpenClaw overwrites on version bumps — there is no gateway.* config knob for it).
Impact
Cosmetic but confusing: nothing is actually broken (the gateway comes back within ~10-20s and the UI mounts fine on reload), but the dialog implies a real failure and points at browser extensions, and the "Keep waiting" affordance is a dead end.
Related
iOS 15.4.1 - Control UI fails to load with 'Control UI did not start' #90716 (iOS 15.4.1 — Control UI fails to load with "Control UI did not start") hits the same fallback surface (12s watchdog, openclaw-app never registers) but from a different trigger — permanent module-parse failure on old WebKit / proxy MIME rewriting, where the bundle can never execute on that platform. This report is a transient failure on a fully-supported browser (desktop Chrome): the bundle is fine, it just wasn't reachable for ~10s during a gateway restart. The two are not duplicates, but they share a root weakness: the fallback's recovery affordances are inadequate. Notably iOS 15.4.1 - Control UI fails to load with 'Control UI did not start' #90716 reports "increased data-openclaw-mount-timeout-ms to 60s — no difference" and "clicked Keep waiting — still fails," which is consistent with fix (1) below — "Keep waiting" never re-attempts the module load, so it cannot recover regardless of trigger. Implementing the auto-retry / real re-fetch would improve both cases.
Environment
2026.6.10(commitaa69b12d0086), built2026-06-24~/Library/LaunchAgents/ai.openclaw.gateway.plist),mode: local, loopback bind, port 18789/opt/homebrew/lib/node_modules/openclawSummary
When the gateway HTTP server is briefly unreachable (e.g. during a service restart), the Control UI's static
index.htmlloads but the app bundle (./assets/index-*.js, ~1 MB) fails to fetch. Theopenclaw-appcustom element never registers, and after the 12s watchdog fires the page shows the "Control UI did not start" fallback dialog.Two problems compound here:
The fallback blames the wrong thing. The dialog text says "A browser extension or early content script may be blocking module execution" and advises a clean profile / disabling extensions. In practice the real cause is a transient module-fetch failure while the gateway was mid-restart — no extension involved. This sends users down a wrong debugging path.
The UI cannot self-recover. Once the initial
<script type="module">fails, the only path back is a full page reload ("Try again"). The "Keep waiting" button re-arms the 12s watchdog but never re-attempts the module download, so even after the gateway is healthy again the UI stays stuck on the dialog until the user manually reloads.Reproduction
KeepAlive=true+ThrottleInterval=10(the default plist OpenClaw generates).Root cause / relevant code
In
dist/control-ui/index.html:armFallbackTimer()sets a 12ssetTimeout(showFallback).customElements.whenDefined("openclaw-app")clears the timer + hides the fallback on success../assets/index-*.jsfails to fetch/execute (gateway down), the element never defines,whenDefinednever resolves, and the timer fires → fallback shown.hideFallback(); armFallbackTimer();— it re-arms the watchdog but does not re-request or re-execute the failed module. A<script type="module">that already failed will not re-run on its own, so this path can never recover.window.location.reload()— the only working recovery.Contributing factor on the gateway side: each app launch produces two SIGTERM→respawn cycles, and
ThrottleInterval=10in the generated plist means each respawn waits ~10s, widening the window in which the UI can load into a dead server. Example from/private/tmp/openclaw/openclaw-YYYY-MM-DD.log:Expected behavior
/or/control-ui-config.jsonand check reachability) would let the dialog say "Gateway is restarting, retrying…" vs. the extension advice.Suggested fixes (any subset)
./assets/index-*.jswith exponential backoff (cache-busting query) before showing the terminal dialog; make "Keep waiting" actually re-attempt the fetch rather than just re-arm the timer.ThrottleIntervalas a config option (currently only editable via the auto-generated plist, which OpenClaw overwrites on version bumps — there is nogateway.*config knob for it).Impact
Cosmetic but confusing: nothing is actually broken (the gateway comes back within ~10-20s and the UI mounts fine on reload), but the dialog implies a real failure and points at browser extensions, and the "Keep waiting" affordance is a dead end.
Related
openclaw-appnever registers) but from a different trigger — permanent module-parse failure on old WebKit / proxy MIME rewriting, where the bundle can never execute on that platform. This report is a transient failure on a fully-supported browser (desktop Chrome): the bundle is fine, it just wasn't reachable for ~10s during a gateway restart. The two are not duplicates, but they share a root weakness: the fallback's recovery affordances are inadequate. Notably iOS 15.4.1 - Control UI fails to load with 'Control UI did not start' #90716 reports "increaseddata-openclaw-mount-timeout-msto 60s — no difference" and "clicked Keep waiting — still fails," which is consistent with fix (1) below — "Keep waiting" never re-attempts the module load, so it cannot recover regardless of trigger. Implementing the auto-retry / real re-fetch would improve both cases.