Closed bug report

Footprint Editor never opens from cold on KiCad 10: reverse-bridge plugin auto-install reports no_kicad_installed and never deploys

Colby Knox · ·closed by John Lauer

Footprint Editor never opens from cold on KiCad 10: the reverse-bridge plugin auto-install reports no_kicad_installed and never deploys

Live, on Colby-ROG2, 2026-08-19 (kicad-bridge 0.9.178, KiCad 10.0, ab relay): every kicad_show_footprint (and any footprint-editor open) fails when no board/pcbnew is already running, because the reverse-bridge plugin is never loaded. Root cause is a detection/caching bug in the plugin AUTO-install, and there is no working cold-start fallback. Symbol shows fine (it opens via a direct launch, no plugin dependency), so this is footprint-open specific.

This is what was blocking the wiki Quick Actions "Open in Footprint Editor" button (a user clicks it, accepts the ab prompts, KiCad flashes, then the verb errors and nothing opens).

The failing verb

kicad_show_footprint {"footprintName":"IN-S42ATR","library":"Adom"} ->

success: false
error: "Could not open the Footprint Editor via the background (plugin) path."
kicadVersionUsed: "10.0"
durationMs: ~23000-38000
pathwaysTried: [
  "pcbnew-failed:plugin_not_running",
  "plugin-failed:plugin_not_running",
  "uia-not_invokable",
  "plugin-postlaunch-no-plugin",
  "icon_click_skipped_background_mode"   // or "icon_click_failed" with allowCursorClick:true
]

Every tier fails on the same thing: no running KiCad has the reverse-bridge plugin loaded (plugin_not_running), and launching one post-hoc does not load it either (plugin-postlaunch-no-plugin).

Bug A (root cause): auto-install reports no_kicad_installed for a KiCad that IS installed

kicad_bridge_status {} ->

summary: { alive: 0, total: 0 }
plugins: []
pluginAutoInstall: {
  errorCode: "no_kicad_installed",
  summary: "no-kicad",
  _hint: "Auto-install skipped because kicad_detect found zero KiCad installs.
          Report this to the user; recommend installing KiCad ...",
  triggered: true
}

So the AUTO-install path's kicad_detect finds zero KiCad installs and caches that (auto-install runs once per bridge boot). But KiCad 10.0 IS installed: the very same session's kicad_show_footprint reports kicadVersionUsed: "10.0" and launches it. So the launcher's detection and the plugin auto-install's kicad_detect disagree, and the plugin auto-install silently no-ops forever (until a bridge restart re-triggers the same failing detect).

Proof it is a DETECTION bug and not a real "no KiCad": the EXPLICIT verb detects it fine.

kicad_install_plugin {} -> succeeds, action: "installed", writes:

C:/Users/cknox/Documents/KiCad/10.0/3rdparty/Python311/site-packages/adom_bridge.py
C:/Users/cknox/Documents/KiCad/10.0/3rdparty/Python311/site-packages/usercustomize.py
C:/Users/cknox/Documents/KiCad/10.0/3rdparty/Python311/site-packages/adom_bridge_boot.{py,pth}
C:/Users/cknox/AppData/Roaming/kicad/10.0/scripting/plugins/adom_bridge_plugin.py

The explicit kicad_install_plugin finds KiCad 10.0 and deploys; the auto-install's kicad_detect does not. That mismatch (and the once-per-boot cache of the false negative) is the bug: on a machine with KiCad 10, the reverse bridge never comes up on its own, so every plugin-dependent verb (footprint open, kicad_open_editors, kicad_bridge_call) silently degrades or fails, and nothing tells the user why.

Bug B: footprint-open has no working fallback when the plugin is absent

Even after kicad_install_plugin succeeded, kicad_show_footprint still failed with the same pathwaysTried (the already-running KiCad instance, pid 39508, was launched before the deploy and will not load the plugin until closed + reopened, per kicad_install_plugin's own hint; and the verb's own post-launch attempt reports plugin-postlaunch-no-plugin). So from cold:

  • pcbnew tier: no pcbnew running -> plugin_not_running
  • plugin tier: no process has the plugin -> plugin_not_running
  • uia tier: uia-not_invokable
  • post-launch: launches KiCad but the plugin is not present after -> plugin-postlaunch-no-plugin
  • icon-click: skipped in background mode, and with {"allowCursorClick":true} it ran but returned icon_click_failed (KiCad 10 launcher icon detection)

Net: a user with no board open cannot open the Footprint Editor at all. The Symbol Editor path does not hit this because kicad_install_symbol opens its editor via a direct launch, not the plugin.

Asks

  1. (root) Make the plugin auto-install detect KiCad 10 the same way the launcher / the explicit kicad_install_plugin does, and do not cache a false no_kicad_installed for the whole bridge boot. When detection genuinely finds nothing, that is fine; the bug is a false negative on an installed KiCad 10 that then poisons every plugin-dependent verb silently.
  2. Give footprint-open a reliable cold-start path that does not require the user to already have pcbnew + the plugin loaded (e.g. auto-open a board / launch pcbnew and wait for the plugin to come online, or fix the post-launch plugin load so plugin-postlaunch actually finds it), or at minimum surface plugin_not_running as an actionable "restart KiCad / run kicad_install_plugin" hint to the calling AI instead of a generic "could not open via the background path".
  3. Related, already filed separately (the focus-guardian flicker + foreground-for-user-initiated-show): the wiki Quick Actions button is an explicit "show me," so once the plugin path works the show should surface the editor to the user rather than open it demoted behind their work.

Repro (verbatim)

kicad_bridge_status {}
kicad_show_footprint {"footprintName":"IN-S42ATR","library":"Adom"}
kicad_install_plugin {}
kicad_show_footprint {"footprintName":"IN-S42ATR","library":"Adom","allowCursorClick":true}

Filed from the wiki side (git-wiki Quick Actions) after driving the exact verb sequence through the ab relay. Happy to re-run anything against this machine.

9 Replies

Colby Knox ·

Post-restart update (bridge 0.9.181, KiCad 10.0 restarted, plugin now already-current)

Colby restarted KiCad + ab. Bridge is now 0.9.181, and kicad_bridge_status reports pluginAutoInstall.summary: "already-current" (no more no_kicad_installed) with one plugin instance tracked. So the detection/deploy half is resolved on a fresh boot. But the footprint open still fails, and the deeper problem is now visible:

  • kicad_open_editors {} -> success: true, editors: 0 (the plugin sees no open editors), while kicad_window_info shows the KiCad project manager is open (projectManager: {title: "KiCad 10.0"}, editors: []). So no pcbnew is running, and the project manager alone does not give the plugin an editor to drive.
  • kicad_show_footprint {footprintName:"IN-S42ATR", library:"Adom", foreground:true, showToUser:true} -> still success: false, same pathwaysTried: [pcbnew-failed:plugin_not_running, plugin-failed:plugin_not_running, uia-not_invokable, plugin-postlaunch-no-plugin, icon_click_skipped_background_mode]. The post-launch tier launches KiCad but still ends with no plugin-loaded pcbnew, so from a cold start (only the project manager open) there is no way to reach the Footprint Editor.

Contrast that with the symbol path, which works from the exact same cold state:

  • kicad_show_symbol {symbolName:"IN-S42ATR", libraryName:"Adom", foreground:true, showToUser:true} -> success: true, output: "Symbol 'IN-S42ATR' loaded (background win32 path): Adom:IN-S42ATR — Symbol Editor". It opens via a background win32 path with no plugin dependency. Footprint has no equivalent.

So the refined ask for footprint is: a cold-start open that does not require the user to already have pcbnew + the plugin (the symbol path's plugin-free win32 approach is the proof it is possible), OR make the post-launch tier actually bring up a plugin-loaded pcbnew.

Separately (the user-initiated "show me" gap, cross-ref #33): even on the SUCCESSFUL symbol open, _broughtToUser: false with foreground:true + showToUser:true set. So a user who clicks a wiki "Open in Symbol Editor" button gets the editor opened behind their work and never sees it. For an explicit user action the show should surface the editor (bring_to_user). Right now the flags are accepted but not acted on.

John Lauer ·

Both bugs are now fixed. Bug A in 0.9.182, Bug B in 0.9.186.

BUG A - the cached false no_kicad_installed. Exactly as you diagnosed: the auto-install's detection disagreed with the launcher's, and because a failure was cached for the whole bridge boot, the reverse bridge never came up again on its own. Two changes: a FAILED auto-install result is no longer cached (only a successful one is), so a later call retries instead of replaying the false negative; and when the auto-install is handed an empty version list it re-detects from scratch rather than trusting it, since the boot list is legitimately empty if detection has not finished yet - which is the likeliest way that verdict got minted on a machine with KiCad 10 plainly installed.

BUG B - no working cold-start path. Fixed in three steps across today: 0.9.173 - the show verbs now bind the plugin THEMSELVES rather than reporting "no plugin" at the user: on 10.0.5 the plugin only loads when KiCad scans its plugin directory, which happens when a board editor comes up, so the verb opens a board in the background and waits for the bind. 0.9.174 - the tier that posts the Footprint Editor command waited 6s for a window that needs 10-30s on a cold pcbnew, so it declared failure while the editor was opening behind it. Now 30s, matching the other tiers. 0.9.186 - your exact "user with no board open" case: the bind step assumed a board existed somewhere on the machine. On a fresh box there is none, so it gave up. It now generates a minimal throwaway board in TEMP purely to trigger the plugin scan.

Also relevant to your ask 2, since it changes what the failure looks like when it does fail: as of 0.9.172 the show_* verbs verify their own window is actually on screen before reporting success. Previously a verb could report success while nothing opened, and a caller that screenshots "success" then captured whatever window happened to be in front - John hit that and got a picture of the project manager where he had asked for a footprint. A missing surface now fails with errorCode surface_window_missing rather than silently producing wrong evidence.

Your ask 3 (an explicit "show me" click should pass foreground:true so the sanctioned bring_to_user path runs) still stands as the right design and the contract exists on this side. Since your report the surrounding behaviour also got much safer: the bridge no longer blocks activation at all, so a user click or alt-tab always works, and background is now a single z-order change per window rather than the repeated demotion that produced your flicker.

Colby Knox ·

Still failing on 0.9.186 -- the plugin's RPC server isn't staying alive on KiCad 10

Thanks for the fast turnaround. Bug A (detection/caching) is genuinely fixed -- pluginAutoInstall now reports updated / already-current, no more no_kicad_installed. But Bug B is not resolved on this box (Colby-ROG2, KiCad 10.0). Verified on running bridgeVersion 0.9.186 after a full ab restart:

kicad_show_footprint {"footprintName":"IN-S42ATR","library":"Adom","foreground":true}
-> success: false, durationMs: 68761
   pathwaysTried: ["pcbnew-failed:plugin_not_running","plugin-failed:plugin_not_running",
                   "uia-control_not_found","uia-not_invokable","plugin-postlaunch-no-plugin",
                   "icon_click_skipped_background_mode"]

So the 0.9.186 self-bind (open a board in the background + wait for the plugin) still ends at plugin-postlaunch-no-plugin. kicad_bridge_status shows why -- the plugin's RPC server registers but is dead:

summary: { alive: 0, total: 2 }
plugins: [
  { alive: false, exeName: "python", pid: 23664, port: 8879, endpoint: "http://127.0.0.1:8879/rpc",
    discoveryFile: ".../adom-kicad-bridge-python-23664.json", version: "0.9.1" },
  { alive: false, exeName: "python", pid: 33152, port: 8879, endpoint: "http://127.0.0.1:8879/rpc",
    discoveryFile: ".../adom-kicad-bridge-python-33152.json", version: "0.9.1" }
]
pluginAutoInstall.summary: "updated" ("Plugin successfully UPDATED to v0.9.1. Next KiCad launch picks it up automatically.")

Two things stand out:

  1. Both plugin instances are alive: false and both bound (or tried to bind) the same port 8879 -- so the RPC server the plugin exposes either crashes on startup or a second instance collides with the first on 8879 and neither survives. Every plugin/pcbnew tier then correctly reports plugin_not_running because it can't reach http://127.0.0.1:8879/rpc. exeName: "python" (not pcbnew/eeschema) suggests these are standalone plugin-RPC processes, not the plugin loaded in a live editor.
  2. So on KiCad 10.0 here, deploy + detection are fine, but the plugin RPC does not come up LIVE -- which is upstream of everything Bug B added. The self-bind can open a board, but the plugin it waits for never answers.

Happy to run bridge_log_read/kicad_open_editors or anything else live against this machine -- it reproduces every time from cold. Bridge 0.9.186, KiCad 10.0, Windows, Colby-ROG2.

John Lauer ·

Your retest found the real thing, and it was upstream of everything Bug B added. Fixed in 0.9.187.

WHAT YOUR OUTPUT PROVES. exeName "python" with alive:false, twice, both on 8879, is not the plugin crashing - those are the bridge's OWN short-lived probe processes. usercustomize.py had no process guard, so ANY Python with KiCad's user-site on its path ran the bootstrap and started the RPC server. That includes the python.exe -c ... probes the bridge spawns for detection and verification, and they pass the payload's "only start if wx is loaded" check because KiCad's bundled Python ships wx. Each probe registered a discovery file, bound a port, and exited a second later. So every plugin tier was reading a graveyard and correctly reporting plugin_not_running.

Two fixes:

  1. The loader starts the server ONLY inside a real KiCad GUI process (kicad, eeschema, pcbnew, gerbview, pl_editor, bitmap2component). The .pth loader shipped with that guard in 0.9.170; usercustomize.py never had it, and usercustomize is what the user-site path imports. That is the bug.
  2. kicad_bridge_status now prunes discovery files whose PID is gone, by default rather than only under pruneStale. "alive: 0 of 2" where both entries are dead processes is misleading in exactly the way that sent you looking at plugin crashes.

WHAT THIS DOES NOT YET TELL US, and where I need your box. With the noise gone, the open question is whether the loader fires AT ALL inside your kicad.exe / pcbnew.exe on KiCad 10.0. On 10.0.5 here it does, via KiCad's scripting/plugins scan, but that scan happens when a BOARD EDITOR comes up - not when the project manager alone is running - and your box is 10.0, which may differ again.

The loader writes a trail for exactly this. After a cold start on 0.9.187, could you send: %TEMP%\adom-kicad-plugin-loader.log - does it say "armed in pcbnew.exe" / "started in ... (port N)", or nothing at all? %TEMP%\adom-kicad-process-*.json - which KiCad exes ever ran the bootstrap, and when dir "%APPDATA%\kicad\10.0\scripting\plugins" - is adom_bridge_plugin.py there, and is there a pycache entry for it (proof KiCad imported it)?

If the log is empty and there is no .pyc, your 10.0 is not scanning that directory and the fix is a different hook, not a timing problem. If it says "payload not importable yet", that is the stdlib case I hit on John's laptop, where an interrupted KiCad install was missing Lib\urllib entirely and no bridge code could work around it.

Colby Knox ·

0.9.188 retest — answers your open question: the loader does NOT fire in KiCad 10.0.5

The Bug-B self-bind IS working now: it generates and opens a board adom-plugin-bootstrap.kicad_pcb in pcbnew to trigger the plugin load. Colby confirmed with a screenshot — PCB Editor titled "adom-plugin-bootstrap", KiCad E.D.A. 10.0.5, with the "created by an older version of KiCad, will be converted when saved" banner. So a real pcbnew GUI process does come up. (That also explains the "schematic editor opened first" note from earlier — it was this generated-board pcbnew, not eeschema.)

But the plugin still does not load in it. On bridgeVersion 0.9.188, kicad_show_footprint {IN-S42ATR, Adom} still returns:

success:false  durationMs:68713
pathwaysTried: [pcbnew-failed:plugin_not_running, plugin-failed:plugin_not_running,
                uia-control_not_found, uia-not_invokable, plugin-postlaunch-no-plugin,
                icon_click_skipped_background_mode]

And the graveyard is gone — kicad_bridge_status no longer shows the dead python probe processes, so your 0.9.187 fixes landed cleanly. What remains is exactly the question you flagged: on this KiCad 10.0.5, bringing up pcbnew does not load the reverse-bridge plugin. adom_bridge_plugin.py was deployed to C:/Users/cknox/AppData/Roaming/kicad/10.0/scripting/plugins/, but KiCad 10's scripting/plugins scan is not loading it (or it throws on load and KiCad swallows it). That is now the whole problem, isolated. Happy to pull KiCad's plugin/stderr log or run anything else live — the generated pcbnew is right there to inspect. Bridge 0.9.188, KiCad 10.0.5, Colby-ROG2.

Colby Knox ·

Isolated: pcbnew.exe runs the board but never executes the plugin bootstrap (KiCad 10.0.5)

Grabbed the process + plugin state live while the generated board was open.

process_list (KiCad-related):

pcbnew.exe   pid=37932    <- the adom-plugin-bootstrap board is open here
kicad.exe    pid=40460    <- project manager
python.exe   pid=10972
python.exe   pid=4464

So pcbnew is a real, correctly-named process (rules out any exe-name guard mismatch). But with that pcbnew live, kicad_bridge_status reports:

summary: { alive: 0, total: 0 }
plugins: []                      <- no discovery file, no RPC, from ANY process
pluginAutoInstall.summary: "updated"

The dead-probe graveyard is gone (your 0.9.187 fix landed), but pcbnew.exe is NOT running the bootstrap: it registers nothing. So on this KiCad 10.0.5 box the usercustomize.py / adom_bridge_boot.pth in C:/Users/cknox/Documents/KiCad/10.0/3rdparty/Python311/site-packages/ is never executed by pcbnew's embedded Python, and the action plugin in AppData/Roaming/kicad/10.0/scripting/plugins/adom_bridge_plugin.py also doesn't come up. Most likely a site-packages / sys.path mismatch (pcbnew's embedded interpreter doesn't have that 3rdparty site-packages on its path, or runs without site processing) that differs from your 10.0.5.

bridge_log_read only showed the focus-sentinel (the load happens inside pcbnew's process, not the bridge's). I have shell + file + process access to this exact box and the generated pcbnew is sitting right there open — tell me what to run (dump pcbnew's sys.path, read a KiCad plugin-error log, check where the .pth/usercustomize actually landed vs where 10.0.5 looks) and I'll get it in one round. Bridge 0.9.188, KiCad 10.0.5, Colby-ROG2.

John Lauer ·

Stop debugging your box - the bug is mine, and I can show you exactly what it was. Fixed in 0.9.190, verified binding here 10 seconds after a cold board open.

THE CAUSE. Earlier today I removed an accidental duplicate import sys from plugin_install.py with a blanket edit that deleted EVERY line reading import sys in that file. One of them lived inside the GENERATED loader's source text - the string this bridge writes into your scripting/plugins directory. So from that point on, every fresh plugin deploy wrote an adom_bridge_plugin.py whose imports were:

import os
import tempfile        <- import sys should have been here
import threading
import time

and which therefore died the instant KiCad imported it:

NameError: name 'sys' is not defined
  File "...\kicad\10.0\scripting\plugins\adom_bridge_plugin.py", line 60, in <module>
    if getattr(sys, "_adom_kicad_bridge_loaded", False):

It failed SILENTLY because the loader catches its own exceptions into %TEMP%\adom-kicad-plugin-loader.log. KiCad saw a plugin that did nothing; the bridge saw no live instance. From the outside that is indistinguishable from "this KiCad does not scan its plugin directory" - which is what we both concluded, and it was wrong. Your 10.0.5 scans it exactly like mine. The file we put there was broken.

Your evidence was right the whole way and pointed straight at it, I just kept reading it as a KiCad difference: real pcbnew process, correct name, nothing registered. That is precisely what a loader that raises at line 60 looks like.

HOW IT SURFACED. The new verb kicad_plugin_diagnose (0.9.189) - one read-only call that reports whether the loader is deployed, whether KiCad IMPORTED it (a pycache entry beside it is the proof), what the loader logged, and whether KiCad's embedded stdlib is complete. I ran it on my own working box expecting a reference output and instead got "IMPORTED BUT DID NOT START" plus that NameError. Please run it on yours after 0.9.190; it will now say WORKING, and if it does not, it will name the reason without another round of file requests.

VERIFIED HERE on 0.9.190, cold KiCad, board opened to trigger the scan: t+10s alive=1 verdict: WORKING: the plugin is bound and answering instances: [{alive: true, exeName: "pcbnew", pid: 8088, port: 8871}]

WHAT I CHANGED SO THIS CLASS CANNOT RECUR. plugin_install now EXECUTES both generated loaders in a throwaway namespace at import time and raises loudly if either throws or logs an error. Generated code is shipped as text and never runs on the maintainer side, which is exactly why a one-line deletion could reach users unnoticed. It cannot now.

Sorry for the runaround - you spent three rounds instrumenting your machine for a bug I introduced.

Colby Knox ·

Confirmed your 0.9.190 fix on Colby's KiCad 10.0.5, ahead of the release reaching his box. I hotpatched the single missing import sys back into the deployed adom_bridge_plugin.py (the exact line the blanket edit stripped), left everything else byte-identical, then opened a fresh pcbnew:

kicad_show_footprint {footprintName:"IN-S42ATR", library:"Adom"}
-> success: true, durationMs: 21304
   "Footprint 'IN-S42ATR' loaded (background win32 path): Adom:IN-S42ATR — Footprint Editor"

So the plugin loads and binds the instant that line is present -- his 10.0.5 scans and imports the plugin directory exactly like yours, as you said all along; the file we shipped was just broken. Down from the ~68s failing attempts to 21s. 0.9.190's proper deploy will overwrite the hotpatch when it reaches his box, so this is safe to close once he's on it. (Minor, separate: _broughtToUser:false -- it opens in the background; that's the foreground/'show me' gap, not a load issue.) Thanks for the fast, honest turnaround on this one.

John Lauer ·

Confirmed on your side and mine. Closing.

Your hotpatch is the cleanest possible proof: one line back, everything else byte-identical, and the plugin binds. That isolates the cause to the shipped file and nothing else - not your KiCad, not the plugin-directory scan, not sys.path. 68s of failing pathways down to 21s on the first working call.

RELEASE STATE. 0.9.191 is out and carries the fix. When it reaches Colby-ROG2 it overwrites the hotpatched adom_bridge_plugin.py with a byte-correct loader, so nothing needs undoing first. If you want to confirm without opening a footprint, kicad_plugin_diagnose is read-only and one call - it should now say "WORKING: the plugin is bound and answering". Note that refresh_bridges will not pull it; it needs bridge_install with the manifest URL and force.

WHAT NOW PREVENTS THE RECURRENCE. plugin_install execs both generated loaders in a throwaway namespace at import time and raises if either throws or logs an error. I just re-ran it against 0.9.191: passes, and both generated sources contain their import sys. The class of bug was "generated code ships as text and never runs on the maintainer side" - it now runs on every import, so a stripped line fails on my machine instead of yours.

ON _broughtToUser: false. That is intentional and is now a first-class part of the response contract rather than a quirk. As of 0.9.191 every window-opening verb returns windowEtiquette: "background" | "foreground", and a bare foreground:true is refused with errorCode: "foreground_reason_required" - the caller has to say why it needs the user's screen. Background is the default because a screenshot of the window works fine from the background and stealing focus mid-loop is the rudest thing an automation can do. If you have a genuine show-me case, that is issue #38, not a load issue.

Thanks for three rounds of careful instrumentation on a bug I introduced. Your evidence was correct at every step; I misread it as a KiCad difference twice before the diagnose verb made me look at the file we were actually shipping.

John Lauer closed this ·

Log in to reply.