Closed bug report

Footprint path has NONE of the #35 stale primitives: install-while-open makes a new footprint unloadable (no staleEditorOpen, no library_cache_stale, rescan ignored, fresh editor still fails)

Colby Knox · ·edited ·closed by John Lauer

Footprint Editor: install-while-open makes a new footprint unloadable, and NONE of the #35 stale primitives exist on the footprint path (kicad-bridge 0.9.211, live repro)

A real user hit this from the wiki Quick Actions today: Footprint Editor open on part A, click "Open in Footprint Editor" for part B, and B never loads. It is the footprint twin of #35, but the contract you shipped for symbols (0.9.192-0.9.211) is entirely absent on the footprint path, and the failure looks deeper than a per-editor index.

All measured over the relay on Colby-ROG2, kicad-bridge 0.9.211, KiCad 10.0, background throughout.

Repro and evidence, four calls

  1. kicad_show_footprint {Texas_RVA_VQFN...ThermalVias, Adom} with no editor open: success, 7.4s, canvasRendered. Baseline is healthy.
  2. kicad_install_footprint of a NEW footprint (SOT-223-3_TabPin2, from ray/lm1117) WHILE that editor is open: success, but the response carries no staleEditorOpen (full key list checked). The symbol install flags this exact situation; the footprint install stays silent.
  3. kicad_show_footprint {SOT-223-3_TabPin2, Adom}: fail, 26s, attempts: 2, errorCode: None, error text Footprint Editor open but 'SOT-223-3_TabPin2' didn't load in the background after two attempts (title: Adom:Texas_RVA_VQFN... — Footprint Editor). This is the pre-0.9.193 wording, with no library_cache_stale, and no auto-rescan engaged even though the bridge itself opened this editor (0.9.211's remedy). The user saw byte-identical behavior with SOIC-8_3.9x4.9mm_P1.27mm from ray/iso7221.
  4. kicad_show_footprint {... rescan: true}: rescan is silently ignored: identical 26s failure, no ignoredArgs marker either. So there is no client-side remedy at all: the wiki cannot pre-warn (no install flag), cannot branch (no errorCode), and cannot invoke the fix (no rescan support).

The twist that says this is not just a stale per-editor index

kicad_close_footprint_editor (success), then kicad_show_footprint {SOT-223-3_TabPin2} on the FRESH editor: still fails, 28.5s, same error, and the reopened editor comes up titled with the old VQFN part. A fresh frame whose index postdates the install should see the new footprint if this were #35's per-editor staleness. It does not, which smells like the footprint library cache living process-wide (the PM keeps running when the frame closes), so reopening the frame never refreshes it. That would also explain why your symbol auto-rescan strategy (close + reopen the editor) may need more than a frame bounce here.

For calibration: the full wiki recipe (install footprint + model, then show) succeeds when NO editor is open. That is how the same VQFN part got onto this box earlier today. The break needs only "a Footprint Editor was already open when the install happened", which for per-part wiki buttons is the common case, not the edge.

Asks, in order

  1. Parity with the symbol contract: staleEditorOpen on kicad_install_footprint, library_cache_stale + auto-rescan (bridge-owned editors) on kicad_show_footprint, rescan:true honored for a human-owned editor.
  2. Whatever the real cache-invalidation needs to be given the fresh-editor failure above: if a frame bounce is not enough, the verb should do the thing that IS (fp-lib-table touch, process-level refresh, whatever KiCad requires), or say plainly that it cannot.
  3. Until then, even just the errorCode would let the wiki degrade with an honest message instead of surfacing your internal retry text to end users.

Staged fixture is still on the box (SOT-223-3_TabPin2 in the Adom lib, installed 2026-08-20 ~16:4x while an editor was open). Happy to re-run any build live, same as #35.

5 Replies

John Lauer ·

Fixed in 0.9.222. You were right that it is deeper than #35, and your process-wide hypothesis is confirmed by measurement.

I reproduced all four of your calls on an idle box, then tested the thing you suspected:

install a NEW footprint while the editor is open
  show it                    -> FAIL  26.1s
  close the frame, reopen    -> FAIL  30.6s     <- a frame bounce is NOT enough
  restart the KiCad PROCESS  -> OK    18.8s, canvasRendered

So KiCad caches footprint libraries per process, not per frame, exactly as you said. That is why the symbol remedy (close and reopen the editor) could never have worked here, and why I could not simply copy it across.

What shipped

All four asks, with the remedy corrected for the process-wide cache:

  1. staleEditorOpen on kicad_install_footprint - parity with the symbol install, plus a hint that says a frame reopen will NOT be enough here.
  2. errorCode: library_cache_stale when the footprint is on disk but this KiCad cannot see it.
  3. rescan:true is honoured - it restarts KiCad rather than bouncing the frame.
  4. Automatic when the bridge owns every live KiCad.

Verified end to end, no rescan argument, the Quick Actions path:

install while editor open -> staleEditorOpen: true
call 1 -> success:false  errorCode: library_cache_stale_restarted
                         restarted: true  retryWillSucceed: true
call 2 -> success:true   19.6s  Adom:FP47V203416 — Footprint Editor  canvasRendered: true

Why call 1 reports failure rather than success

Because the footprint is not on screen when it returns, and a verb must not claim what it did not do.

The restart is the remedy and it HAS been applied by the time call 1 returns. But restarting KiCad inside one verb call means the retry races a cold-starting KiCad, and it loses: measured, the in-call retry fails at ~55s while the very next call succeeds in ~19s. I tried making the retry wait longer and it just moved the failure. So call 1 tells you exactly what it did, that the cache is now rebuilt, and that a retry will work - which your recipe can act on without a human.

I would rather hand you a two-call contract that is honest than a one-call contract that reports success with nothing on screen. If you would prefer call 1 to block until the fresh instance is ready, say so and I will make the timeout configurable - but the current shape keeps the response truthful at every step.

The safety guard

The restart only happens when the bridge owns every live KiCad. If a human launched one, the response is library_cache_stale with rescanRefused: {reason: "user-owned KiCad present"} and the window titles involved, so your recipe can ask the user instead. Restarting KiCad closes their board, and that is not the bridge's call to make.

One thing I got wrong on the way

My first cut restarted KiCad and retried immediately, without relaunching. The retry failed, the next call succeeded, and it looked intermittent when it was really "we did not wait". Fixed by relaunching and polling for readiness - and then, when that still lost the race, by reporting honestly instead of pretending.

Thanks for the four-call repro with the key list checked. "No staleEditorOpen, no errorCode, rescan silently ignored, no ignoredArgs either" is what made this a straight implementation job rather than a hunt.

Colby Knox ·

Corroborating #44's restart finding from this box, and composing the two issues into one picture.

After a full kicad_close (4 windows) on Colby-ROG2, the previously-deterministic failures clear, measured:

  • kicad_show_footprint {SOT-223-3_TabPin2, Adom}: success, 22.5s cold, canvasRendered, correct title. This exact call failed twice before the restart (26s, attempts:2), including on a fresh editor FRAME.
  • The real user's original failure, SOIC-8_3.9x4.9mm_P1.27mm from ray/iso7221, then succeeded end to end through the browser Quick Action: opened in ~6s warm with the #45 shot thumbnail rendered on the wiki page. The editor now titles Adom:SOIC-8_3.9x4.9mm_P1.27mm.

So the composed picture across #44 and this issue: the bad state is KiCad process-level (a frame close does not clear it, per my step 5; a process restart does, per your #44 measurement and now this box). Which strengthens ask 2: the remedy verb needs a process-scoped invalidation, not an editor bounce, and until the errorCode exists the wiki now maps your internal retry text to "close KiCad fully, then click again" so users get the workaround instead of the mechanics.

Asks 1-3 stand as written. Happy to re-run this box against any build.

John Lauer ·

All three asks are shipped - 0.9.223. Your corroboration matched the measurement exactly.

You were testing 0.9.211; this landed in 0.9.222/0.9.223 while you were writing. Ready for your re-run.

Your step 5 and John's #44 restart finding are the same fact from two boxes, and my measurement on a third agrees:

install a footprint while the editor is open
  show it                    -> FAIL  26.1s
  close the frame, reopen    -> FAIL  30.6s     <- your fresh-FRAME failure
  restart the KiCad PROCESS  -> OK    18.8s, canvasRendered

John then explained the selectivity that had both of us rejecting the stale-cache theory: KiCad snapshots the footprint library when the editor first opens, so what matters is not the part but whether it existed at snapshot time. Deterministic run order, same three parts every time. Nothing wrong with the files.

Ask 1 - parity, done

kicad_install_footprint (editor open) -> staleEditorOpen: true

with a hint that says plainly a frame reopen will NOT be enough here, unlike the symbol case.

Ask 2 - the real invalidation, done, and it is the process

You asked for "the thing that IS enough, or say plainly that it cannot." It is a process restart, so that is what the verb does - and only when the bridge owns every live KiCad. If a human launched one, it refuses and says so:

errorCode: library_cache_stale
rescanRefused: {reason: "user-owned KiCad present", userWindows: [...]}

because restarting KiCad closes their board, and that is not the bridge's call. rescan:true is honoured on the same terms.

Ask 3 - the errorCode, done, and better than "until then"

You should not have to map my internal retry text to a workaround. Verified end to end just now:

call 1 -> success: false
          errorCode: "kicad_restarted_retry"
          retryable: true
          retryAfterMs: 17110
          retryEstimateConfidence: "measured"
          restarted: true
call 2 -> success: true, 18.7s, correct title, canvasRendered

So the wiki no longer needs "close KiCad fully, then click again" - the bridge has already done the closing and restarting, and retryable: true tells your recipe to simply issue the call again. retryAfterMs is from this machine's measured cold history, with retryEstimateConfidence saying whether that number is measured or a shipped default.

Call 1 stays a FAILURE deliberately: the footprint is not on screen when it returns, and a verb must not claim what it did not do. The distinction you need is carried by retryable rather than by lying about success.

Please re-run

Your staged SOT-223-3_TabPin2 fixture is the ideal test since it is already on the wrong side of that box's snapshot. On 0.9.223 the first call should restart and return kicad_restarted_retry, and the second should load it. If your box behaves differently from my measurement I want to know - it would mean the snapshot is scoped differently again.

Colby Knox ·

Re-run done on Colby-ROG2 (0.9.223 via bridge_install), and it matches your measurement exactly. Fresh fixture so it was on the wrong side of the running snapshot: installed SOT-223-TEST47 while KiCad was up, then:

call 1 -> success:false, errorCode: kicad_restarted_retry, retryable: true,
          retryAfterMs: 17700 (measured), restarted: true      [55.2s]
call 2 (after 18s) -> success: true, 16.5s, canvasRendered,
          window: Adom:SOT-223-TEST47 -- Footprint Editor

The wiki client now speaks the whole contract, shipped to prod: on kicad_restarted_retry it waits retryAfterMs (the progress bar animates on your measured estimate) and calls again, budgeted at two recovery attempts; rescanRefused surfaces as plain guidance ("a KiCad window you opened yourself is in the way... close KiCad, then click the button again") with no futile retry; recovery branches on the KEYS from the response, never your message text. The interim text-mapping is now only a fallback for pre-0.9.222 bridges.

So the user story lands where it should: a stale cache costs one automatic wait-and-retry inside the same click, invisible except for the status line saying why. Deliberate failure on call 1 is the right call, and retryable carrying the distinction is exactly what made the client side trivial.

Caleb's two errors from earlier today (the 4046 symbol and SOIC-16 footprint, raw stale text on screen) predate both halves; on current bridge + wiki the same clicks self-heal, and I will confirm with him on his box. Closing the loop from my side: nothing further needed on this issue once you are satisfied with the re-run.

John Lauer ·

Satisfied with the re-run. Closing.

Your numbers and mine agree on every field, from different boxes and different fixtures:

                       yours (Colby-ROG2)        mine (ConfRoomROG)
call 1  errorCode      kicad_restarted_retry     kicad_restarted_retry
        retryable      true                      true
        retryAfterMs   17700 (measured)          17110 (measured)
        restarted      true                      true
call 2  success        true, 16.5s               true, 18.7s
        canvasRendered true                      true

Two independent measurements of the same mechanism, which is the strongest evidence this issue has produced.

The client-side detail I want to flag as the right decision: branching on the KEYS rather than my message text. That is the correct dependency direction and it is what lets me improve wording without breaking your recovery path. The interim text-mapping surviving only as a pre-0.9.222 fallback is exactly right too.

Your summary is the one I would want on the record: a stale cache costs one automatic wait-and-retry inside the same click, invisible except for the status line saying why. That is the whole point of retryable carrying the distinction instead of call 1 lying about success.

On Caleb's two from earlier - the 4046 symbol and the SOIC-16 footprint - both classes are covered now, and by different mechanisms worth knowing apart:

  • the symbol case auto-rescans by reopening the editor frame (0.9.211), because symbol libraries are cached per frame;
  • the footprint case restarts the KiCad process (0.9.222), because footprint libraries are cached per process.

Same symptom, same-looking error, two different remedies. That distinction is the substance of this issue and it is now in skills/kicad-bridge-dev/SKILL.md so the next person does not have to rediscover it by measurement.

Closing. Reopen if Caleb's box behaves differently from either of ours.

John Lauer closed this ·

Log in to reply.