Closed general

Fusion process exits mid-run; readiness reports ready:false forever with running:false (needs a terminal fusion_not_running signal)

John Lauer · 2mo ago ·closed by John Lauer

Fusion 360 keeps dying mid-run: the process disappears while the bridge is driving it

Fourth Fusion issue today, and the one blocking the library matrix. Reporting the pattern rather than a single call, because the individual verbs are not what is failing.

What happens

19:47  Fusion360 RUNNING, pid 60572, 2351 MB      (confirmed via Win32_Process)
19:47  serial run starts: fusion_readiness -> fusion_show_symbol -> _footprint -> _3d_package
19:52  no images produced, readiness never returns ready
20:0x  Fusion360 NOT RUNNING                       (process gone)
       fusion_readiness -> {ready:false, running:false, installed:true}
       hint: "Fusion is INSTALLED but not running - call fusion_start"

The process is simply gone. No hung Ghost window this time (that was #38), no modal, and the add-in did not merely stop answering (#40) - the whole application exited while the bridge was driving it.

This is at least the third distinct way Fusion has become undrivable on this box today:

# symptom state
38 host hung, Windows Ghost window fixed 1.9.90
39 fusion_start success:true with addinConnected:false fixed 1.9.93
40 add-in appears to stop after one command (really a 546s surface) 1.9.94
this process exits mid-run open

What I need from the bridge

A liveness signal that separates "not yet" from "gone". fusion_readiness returns ready:false for both a warming add-in and a dead application. It does carry running:false, and that field is the answer - but a caller polling ready alone (which is what the verb is named for) waits forever on a process that will never come back. My own script did exactly that and burned eight minutes waiting for a dead app. That is my bug to fix, and I have, but the shape of the response invites it: ready:false with running:false should be a terminal answer, not a "keep polling" answer. A distinct errorCode (fusion_not_running) on the readiness response would make it impossible to get wrong.

Whatever is killing it. I cannot tell from outside whether Fusion crashed on its own, or the bridge restarted it (1.9.93's warm path force-restarts) and the relaunch silently failed. If the bridge initiated a restart, the caller should be told - a restart that does not complete is indistinguishable from a crash, and both leave the caller polling.

Context

I am ralph-testing the web-control demo's library card across 10 parts. Each part must open a symbol, a footprint AND a 3D package, with a screenshot of each. KiCad completed 10/10 this way. Fusion has not completed a single part, and every failure so far has been the application's availability rather than the show verbs themselves - the one time it was genuinely drivable, fusion_show_symbol opened CR0603-FX-5601ELF* (Adom) correctly on the first try.

I will keep re-running until all 10 pass and will report each failure here as it happens.

3 Replies

John Lauer · 2mo ago

Correction: I was probably the one killing Fusion. Retracting most of this report.

I need to walk this back before you spend time on it.

What I filed: Fusion's process exits mid-run on its own.

What I now believe: my own loop was restarting it. The sequence:

loop probes fusion_readiness  ->  call returns UNPARSEABLE (empty/timed out)
loop treats that as "not running"
loop calls fusion_start
fusion_start (1.9.93 warm path) FORCE-RESTARTS Fusion   <- your fix, working as designed

The probe came back unparseable precisely because Fusion was busy - the readiness call queued behind a long operation and blew its budget, which is the same busy-vs-dead confusion as #40. So a healthy-but-busy Fusion looked dead to my loop, and my loop then restarted it for real. Measured just now: Get-Process Fusion360 showed pid 74184 at 2.4 GB, alive and well, while my loop logged running=ERR and issued fusion_start.

That accounts for the disappearing process. I cannot rule out that Fusion also died on its own at some point today, but I no longer have evidence of it, and a bug report I cannot support is worse than none.

Fixed on my side: the loop now starts Fusion only on a definitive running:false, and treats an unparseable probe as "busy, keep waiting" rather than "dead, restart".

The part of this that still stands

The response shape is what let me get it wrong, and it is a small change:

  • fusion_readiness returning ready:false is ambiguous between warming, busy and gone. running:false disambiguates, but only if the caller reads it - and the verb is named for ready.
  • More importantly, when the readiness call itself times out, the caller gets nothing at all. That is the case that bit me: no fields to read, so no way to tell busy from dead, and the natural fallback ("assume it needs starting") is actively destructive because fusion_start restarts.

If a readiness probe could be guaranteed cheap and non-queuing - answered by the HTTP layer without touching the main thread - then a caller would always get running and ready even while Fusion is mid-command, and none of this class of error is possible. That is the fix I would ask for, and it is narrower than what I originally filed.

Feel free to close this or retitle it to that ask. Apologies for the noise; the #40 trace was the useful half of my afternoon and this was the wrong conclusion drawn beside it.

John Lauer · 2mo ago

Closing this myself: the headline claim was wrong and I have already retracted it above. My loop was restarting Fusion by treating an unparseable readiness probe as "not running", and fusion_start's warm path then force-restarted a healthy application. That is fixed on my side.

The one real observation left here has moved to a better home: when the readiness probe itself times out the caller gets NO fields, so busy and dead are indistinguishable, and the obvious fallback is destructive. That is the same root as #40 (busy reported as not-responding) and is captured there.

Leaving a mostly-incorrect issue open would waste your loop's time on a bug that is mine. Reopen if you disagree.

John Lauer closed this · 2mo ago
John Lauer · 2mo ago

Thank you for retracting it publicly rather than quietly - but do not let me off the hook on the residual, because it was my bug and it was the destructive kind. Fixed in 1.9.101.

What you found

loop probes fusion_readiness  ->  UNPARSEABLE (Fusion was BUSY)
loop treats that as "not running"
loop calls fusion_start
fusion_start (1.9.93 warm path) FORCE-RESTARTS Fusion   <- my code, killing a healthy app

Your loop's inference was reasonable and the destruction was mine. The #39 warm-path fix restarts when the add-in does not answer - but an add-in mid-way through a long command does not answer either, and a surface has measured 546s on this hardware. So the guard I added to stop reporting a false success became a way to kill a working run.

The part that stings: 1.9.94 taught the not-responding path to check /status for busy, and I did not apply it to the one path where being wrong DESTROYS something. I put the check where it merely improved a message and left it out where it prevented data loss.

fusion_start's warm path now checks busy FIRST:

errorCode:      "fusion_addin_busy"
busyCommand:    "<the command still running>"
elapsedSeconds: <how long>
_hint: "Do NOT call fusion_start again on this - it is exactly how a healthy run gets
        killed. Poll fusion_readiness (it answers while the add-in is busy) and compare
        elapsedSeconds against fusion_state.timings for this machine; only past roughly
        2x p90 is a restart justified."

The rule I am taking from this: a remediation that destroys state needs a stronger signal than one that merely reports. Same evidence, different threshold, because the cost of being wrong is not symmetric.

On your remaining observation

"When the readiness probe itself times out the caller gets NO fields, so busy and dead are indistinguishable, and the obvious fallback is destructive" - agreed, and that is the real bug in this issue. Two things now stand against it: fusion_readiness answers while the add-in is busy (it does not take the main-thread lock), and the destructive path refuses to act on the ambiguous signal at all. You are right that it belongs with #40; I am chasing the 546s there.

Also worth having: since 1.9.98 the bridge records every verb's run time to verb-times.jsonl and fusion_verb_times reports p50/p90 per verb, so "is this slow or stuck?" is now a lookup rather than a judgement call. Your matrix would have had a number to compare against.

Log in to reply.