What happened
Installing a Desktop update never completes. The app keeps running with its Runtime Host already retired, so every Host-backed IPC channel fails and the UI empties out: the Projects list renders "No project" and refreshing model connections reports a network error. The error is misleading — nothing is wrong with the network.
The update itself is never applied. /Applications/Maka.app stays on the previous version, so the next launch offers the same update and fails the same way. The only way out is to quit and relaunch manually.
This reproduces on every install attempt on this machine.
Expected: the app quits, Squirrel replaces the bundle, and the app relaunches on the new version.
How to reproduce
- Run a packaged nightly build (observed on
0.2.0-dev.39.20260916).
- Let the updater find a newer nightly, or trigger a check from Settings → About.
- Click install once the update is downloaded and verified.
- The window stays open. Within ~15s the Projects list shows "No project" and a "刷新模型连接失败 / 网络错误" toast appears.
- The process never exits.
~/Library/Caches/com.maka.desktop.ShipIt/ShipIt_stderr.log shows ShipIt waiting for it.
- Quit manually and relaunch. The app works again, but the bundle is still the old version.
Environment
- Maka version:
0.2.0-dev.39.20260916 (packaged, nightly channel)
- OS: macOS 25.6.0 (darwin, arm64)
- Surface: Desktop
- Electron 43.4.1 / Chrome 150.0.7871.224 / Node 24.18.1
Logs, screenshots, or additional context
ShipIt, showing it wait ten minutes for a process that never exits, then abort because the app came back (local time):
2026-09-20 10:43:28.878 ShipIt[23809] Detected this as an install request
2026-09-20 10:53:37.123 ShipIt[23809] Beginning installation
2026-09-20 10:54:01.154 ShipIt[23809] Aborting update attempt because there are 1 running instances of the target app
2026-09-20 10:54:01.154 ShipIt[23809] Installation cancelled: Error Domain=SQRLInstallerErrorDomain Code=-9 "App Still Running Error"
The same failure occurred on an earlier attempt that day (10:03:15 → 10:03:58), which is why it reads as always reproducible rather than a race.
Desktop diagnostic report from the same run (UTC, +8h offset from the log above). The Host exits cleanly at 10:43:23 local, and the first IPC failure lands exactly 15s later — DEFAULT_REPLACEMENT_WAIT_TIMEOUT_MS:
[2026-09-20T02:43:23.914Z] INFO [runtime-host] candidate exited cleanly { pid: 68872, code: 0, signal: null }
[2026-09-20T02:43:38.857Z] ERROR Error occurred in handler for 'skills:listInvocable': RuntimeHostHandlerUnavailableError
[2026-09-20T02:50:48.218Z] ERROR Error occurred in handler for 'projects:getSnapshot': RuntimeHostHandlerUnavailableError
[2026-09-20T02:50:48.200Z] ERROR Error occurred in handler for 'connections:getSnapshot': RuntimeHostHandlerUnavailableError
Runtime Host connections (1)
"local": reconnecting
Main process uptime: 2856s
Two details narrow it down:
- The report was captured at 02:51 UTC with
uptime 2856s, i.e. the process started at 02:03 and never restarted. The window was still visible, so prepareToQuit() never reached its browserWindow().destroy().
- There is not a single
[runtime-host] reconnecting: line after the Host exits. The reconnect lifecycle was quiesced by the retirement and never rescheduled, which is why the state stays reconnecting forever without producing an error.
Root cause
installUpdate retires the Host (quiesce() + candidateLaunchBarrier.pause()) before calling quitAndInstall, and that retirement only unwinds through the rollback stored in installHandoff. The quit that was supposed to follow never finishes, because prepareToQuit() and cleanup() are awaited without any upper bound. Once a stage stops settling, phase stays in preparing/cleaning and every later quit request is swallowed by if (phase !== 'running') return after the event was already prevented.
Squirrel's ShipIt is an independent process that only waits for the target to exit, so a quit that never finishes blocks the install indefinitely with nothing to read. The App Still Running Error at the end is a consequence, not the cause: by the time the user gives up and relaunches, ShipIt has finally started installing and finds the app running again.
What happened
Installing a Desktop update never completes. The app keeps running with its Runtime Host already retired, so every Host-backed IPC channel fails and the UI empties out: the Projects list renders "No project" and refreshing model connections reports a network error. The error is misleading — nothing is wrong with the network.
The update itself is never applied.
/Applications/Maka.appstays on the previous version, so the next launch offers the same update and fails the same way. The only way out is to quit and relaunch manually.This reproduces on every install attempt on this machine.
Expected: the app quits, Squirrel replaces the bundle, and the app relaunches on the new version.
How to reproduce
0.2.0-dev.39.20260916).~/Library/Caches/com.maka.desktop.ShipIt/ShipIt_stderr.logshows ShipIt waiting for it.Environment
0.2.0-dev.39.20260916(packaged, nightly channel)Logs, screenshots, or additional context
ShipIt, showing it wait ten minutes for a process that never exits, then abort because the app came back (local time):
The same failure occurred on an earlier attempt that day (10:03:15 → 10:03:58), which is why it reads as always reproducible rather than a race.
Desktop diagnostic report from the same run (UTC, +8h offset from the log above). The Host exits cleanly at 10:43:23 local, and the first IPC failure lands exactly 15s later —
DEFAULT_REPLACEMENT_WAIT_TIMEOUT_MS:Two details narrow it down:
uptime 2856s, i.e. the process started at 02:03 and never restarted. The window was still visible, soprepareToQuit()never reached itsbrowserWindow().destroy().[runtime-host] reconnecting:line after the Host exits. The reconnect lifecycle was quiesced by the retirement and never rescheduled, which is why the state staysreconnectingforever without producing an error.Root cause
installUpdateretires the Host (quiesce()+candidateLaunchBarrier.pause()) before callingquitAndInstall, and that retirement only unwinds through the rollback stored ininstallHandoff. The quit that was supposed to follow never finishes, becauseprepareToQuit()andcleanup()are awaited without any upper bound. Once a stage stops settling,phasestays inpreparing/cleaningand every later quit request is swallowed byif (phase !== 'running') returnafter the event was already prevented.Squirrel's ShipIt is an independent process that only waits for the target to exit, so a quit that never finishes blocks the install indefinitely with nothing to read. The
App Still Running Errorat the end is a consequence, not the cause: by the time the user gives up and relaunches, ShipIt has finally started installing and finds the app running again.