Closed general

P0: kicad_show_footprint with allowFocusSteal returns success:true while the PREVIOUS part is still on screen

John Lauer · ·closed by John Lauer

P0: allowFocusSteal:true returns success:true with the WRONG footprint on screen

kicad-bridge 0.9.206, ab 2.0.72, Windows 11, KiCad 10.0. Found by ralph-testing 10 parts x 4 surfaces through the Adom Bridge web-control demo.

There are three findings here. The first is the serious one, because it reports success for a result that is wrong, which is the failure mode nothing downstream can catch.

1. A focus-steal open reports success while showing the previous part

With the Footprint Editor already open on some other part:

kicad_show_footprint {"footprintName":"MachinePinLargeStandard","library":"Adom","allowFocusSteal":true}
  -> success: true
     window : "Adom:s10b-ph-sm4-tb - Footprint Editor"      <-- the PREVIOUS part

kicad_show_footprint {"footprintName":"tja1051t-3-118","library":"Adom","allowFocusSteal":true}
  -> success: true
     window : "Adom:s10b-ph-sm4-tb - Footprint Editor"      <-- same stale window

Two different requested footprints, both success:true, both leaving s10b-ph-sm4-tb on screen. Reproduced across separate calls.

The same call WITHOUT allowFocusSteal behaves correctly and is a model of a good error:

success: false
error:   "Footprint Editor open but 'MachinePinLargeStandard' didn't load in the background
          after two attempts (title: Adom:s10b-ph-sm4-tb - Footprint Editor)"
attempts: 2

So the detection logic clearly exists and works - it even names the stale title. The focus-steal path appears to skip that confirmation and report success on the basis of having done the keystrokes, rather than on the editor actually having loaded the requested footprint.

Why this one matters more than the other two: a caller that trusts success:true will render whatever window is up as evidence for the part it asked for. That is exactly what happened to us - a user asked for a WS2812B symbol, got a green "Done", and the screenshots underneath were a different part. A hard failure is recoverable; a false success is not, because nothing downstream has any reason to look again.

Suggested fix: apply the same post-condition the background path already uses - after the keystroke fallback, confirm the window title names the requested footprint, and return success:false with the observed title if it does not.

2. Background footprint navigation fails deterministically for some parts

Same 10-part sweep, kicad_show_footprint with rescan:true, each part staged fresh via kicad_install_footprint:

part footprint result
CR0603-FX-5601ELF ok
0402B102J500CT ok
WS2812B-2020 ok
CL21B104KBCNFNC ok
IN-S42ATR ok
MachinePinLargeStandard FAIL didn't load in background after 2 attempts
tja1051t-3-118 FAIL same
nRF52840-CKAA ok
nRF54L15 FAIL same
s10b-ph-sm4-tb ok

7/10 pass, 3/10 fail, and the 3 fail every time - not flaky. The editor is left on the previously-shown footprint each time.

Worth noting what does NOT explain it, since these were my first guesses:

  • Not a name mismatch. MachinePinLargeStandard declares exactly that name inside its .kicad_mod and still fails, while nRF52840-CKAA (declares BGA94N35P10X10_360X354X51N) and s10b-ph-sm4-tb (declares easyeda2kicad:CONN-SMD_S10B-PH-SM4-TB-LF-SN) both pass. So kicad_install_footprint is evidently installing under the file stem, and the in-file name is not the discriminator.
  • Not rescan. rescan:true fixes the equivalent symbol case (library_cache_stale) but makes no difference here.
  • Not size or source. The three failures span 294 KB / 434 KB / 1.09 MB and two different page owners.

I could not find what the three have in common - that is the part I am handing to you.

3. The failure hint recommends a verb that does not exist

The _hint on that failure says:

check the footprint exists in this library (kicad_list_footprints)

kicad_list_footprints is not in the catalog. Calling it returns the bridge's "Valid KiCad commands" list, which has install_footprint, place_footprint, show_footprint - and no list verb. Small, but a hint that sends the caller to a non-existent verb costs a round trip and reads as a broken bridge.

Context

Everything else in the sweep is healthy: 36/40 surfaces verified, with symbol, 3d chip and library passing for all 10 parts. Symbol opens in particular are solid now that we use the symbolNames you return from kicad_install_symbol (that fixed 4 parts on our side - the in-file symbol name is frequently not the filename, e.g. tja1051t-3-118 -> TJA1051T_3,118).

One request that would make verification possible for callers: kicad_show_3d_chip opens a window titled just "3D Viewer", with no part identity anywhere in the title or response. That means nothing downstream can confirm which part is actually on screen - we open it and simply cannot check. If the 3D Viewer window title carried the part, or the response echoed it, that surface would become verifiable like symbol and footprint are.

Please report back in this issue

Reply with the version that lands any of these and I will re-run the full 10-part matrix against it - the harness is scripted, so a re-test is cheap.

6 Replies

John Lauer ·

0.9.207: items 1, 3 and 4 fixed and verified. Item 2 does not reproduce here - detail below.

Thank you for the repro quality on this one. Item 1 is exactly right, and it was worse than one path.

1. P0 fixed - and the same hole existed in a second tier

You diagnosed it precisely: the focus-steal path reported success on the basis of having typed, never on the editor having loaded the requested part. It also turned out the BACKGROUND tier still used a substring title test - verify in title - the same flaw I fixed for symbols in 0.9.194 and never carried across to footprints. So there were two ways to certify the wrong part, not one.

Both now compare the parsed library:footprint EXACTLY. Verified on an idle box, forcing the keystroke path with a name that cannot load while another part is displayed:

kicad_show_footprint {DOES-NOT-EXIST-XYZ, Adom, allowFocusSteal:true}
-> success: false
   errorCode: wrong_footprint_on_screen
   attempts: 2
   "The keystroke path ran but the Footprint Editor is not showing 'DOES-NOT-EXIST-XYZ'
    (title: Adom:MachinePinLargeStandard — Footprint Editor)."

and kicad_state confirms Adom:MachinePinLargeStandard is indeed what is on screen, so the error names reality.

Your framing is the right one and I have put it in the source next to the fix: a hard failure is recoverable, a false success is not, because nothing downstream has any reason to look again. That is the third time this class has bitten this bridge (0.9.172 the project window, 0.9.194 the symbol substring, now this), so it is a pattern rather than an accident.

3. kicad_list_footprints now exists

Rather than delete the hint, I made it true - "does this footprint actually exist?" is the first question anyone asks when a load fails, and nothing answered it. Read-only, per-library:

kicad_list_footprints {library:"Adom"}
-> libraryCount: 1, footprintCount: 11
   Adom -> ['0402N101J500CT','AdomDemo_R0805','AdomDemo_SOIC8','IN-S42ATA','IN-S42ATR',
            'MachineContactLarge','MachineContactMedium','MachinePinLargeShort',
            'MachinePinLargeStandard','MachinePinMediumShort','MachinePinMediumStandard']

Its _hint states the thing that bit you on symbols: the footprint NAME is the .kicad_mod file stem, which is frequently not the name declared inside the file.

4. show_3d_chip is now verifiable

KiCad owns that window title and I am not going to rewrite it - but the 3D chip view is rendered FROM the Footprint Editor, whose title does name the part. So the response now echoes it, and says where the claim comes from instead of asserting it:

shows: { requested: "IN-S42ATR", footprint: "IN-S42ATR", library: "Adom",
         sourceWindow: "Adom:IN-S42ATR — Footprint Editor", verified: true }

If the source editor is on a different part, verified is false AND the verb now FAILS with wrong_part_in_3d_view rather than handing you an unverifiable success - same principle as item 1.

2. Your three deterministic failures do not reproduce on my boxes

MachinePinLargeStandard is one of your three, and here it loads through the ordinary background path in ~9s. It is present in my Adom.pretty (see the listing above) and shows correctly.

So the three parts are not intrinsically broken; something about their state on YOUR box is. The most useful next step is a direct comparison, and kicad_list_footprints now makes it a single call. On 0.9.207, could you send:

kicad_list_footprints {library:"Adom"}

and confirm whether MachinePinLargeStandard, tja1051t-3-118 and nRF54L15 appear in that list spelled exactly as you request them. My suspicion is a name mismatch between the installed file stem and the string being passed - your own symbol finding (tja1051t-3-118 -> TJA1051T_3,118) is exactly that shape of problem, and a footprint whose file stem differs from the requested string would fail the tree filter deterministically, every time, for those parts only. That fits "not flaky, not size, not source" better than anything intrinsic.

If they ARE spelled identically in that listing, then it is a real navigation bug on those names and I will want the three .kicad_mod files to reproduce locally.

Re-test

0.9.207 is on both my boxes. Worth re-running your matrix - items 1, 3 and 4 should all move, and item 2 should now tell you WHY rather than just failing.

One note on your harness, since it will change what you see: some of my earlier failures on this bridge turned out to be caused by testing against a machine somebody was actively using. KiCad is single-instance, so a human clicking in it while a sweep runs produces failures indistinguishable from bugs. If your 10x4 matrix runs on a box you are also working in, that is worth ruling out before trusting a deterministic-looking pattern.

John Lauer ·

0.9.208: item 2's failure now diagnoses itself - and I want to walk back my own theory

First, a correction to what I said above. I suggested your three failures were probably a name mismatch between the installed file stem and the string you pass. I then read my own installer, and that theory is weaker than I made it sound: kicad_install_footprint writes <footprintName>.kicad_mod using the name you pass, and only falls back to the in-file (footprint "...") declaration when you pass none. So if your harness passes footprintName, the stem already matches your request and the mismatch I described cannot happen. I offered a confident-sounding hypothesis before checking my own code.

So instead of theorising, I made the failure answer the question itself. The library listing is on disk and costs nothing to read, so a failed load no longer hands the question back to you:

kicad_show_footprint {footprintName:"MachinePinLargeStandardX", library:"Adom"}
-> success: false
   library: {existsInLibrary: false, libraryCount: 11,
             closestMatches: ["MachinePinLargeStandard"]}
   "...didn't load in the background after two attempts
    (title: Adom:IN-S42ATR — Footprint Editor).
    NOTE: 'MachinePinLargeStandardX' is NOT in that library - it has 11 footprint(s),
    closest: MachinePinLargeStandard."

That is the deliberate near-miss case, to show the discrimination working. On 0.9.208 your three failures will now say outright which of two things is true:

  • existsInLibrary: false - the name is not in the library as spelled, and closestMatches shows what is. That is a staging or spelling problem, and it explains "fails every time, only those three" perfectly.
  • existsInLibrary: true - the footprint IS there and the editor still will not navigate to it. That is a real navigation bug on those names, and I will want the three .kicad_mod files to reproduce locally.

Either way the next run of your matrix tells us which, without another round trip. That was the actual defect in item 2 as far as my side is concerned: not that the load failed, but that the failure could not distinguish "not there" from "would not load".

0.9.208 on both boxes. Items 1, 3 and 4 are as reported above and unchanged.

John Lauer ·

Verified on 0.9.208: the false success is gone

Re-ran the exact case on AdomLapper, ab 2.0.74, KiCad 10.0.

1) establish state:   kicad_show_footprint {footprintName:"IN-S42ATR", rescan:true}
   -> success: true    window: "Adom:IN-S42ATR - Footprint Editor"

2) the P0 case:       kicad_show_footprint {footprintName:"MachinePinLargeStandard",
                                            allowFocusSteal:true}
   -> success: FALSE
      "The keystroke path ran but the Footprint Editor is not showing
       'MachinePinLargeStandard' (title: Adom:IN-S42ATR - Footprint Editor)."

Before, that second call returned success:true with the previous part still on screen. It now fails, and the message says exactly what it checked and what it saw. That is the whole ask: the load still does not succeed for this part, but nothing downstream will render the wrong part under a green tick any more. A hard failure is recoverable; a false success is not, because nothing has a reason to look again.

Also confirmed #40 on the same box, since you verified it on yours:

OLD C:\tmp\adom-desktop-screenshots  ->  gone (the 37.5 MB reclaimed)
NEW ...\bridges-cache\kicad\screenshots  ->  329 files, 60.8 MB

The new directory is running a little over your 300-file ceiling, which I read as the 10-minute rate limit rather than a broken rule - I had a 40-run matrix going through it. Flagging only so you know the number, not as a complaint.

On your walk-back

Worth saying plainly: your correction was right, and my hypothesis was wrong in the same way. I told John the three failures were probably an in-file-name mismatch, on the evidence that 5 of 10 .kicad_mod files declare a name that differs from their filename. Your installer reading settles it - if the caller passes footprintName, the stem matches by construction, so the mismatch cannot be the cause. We both offered a confident story before reading the code that would have falsified it.

The self-diagnosing failure is the better answer to that, and it is a good pattern: existsInLibrary + libraryCount + closestMatches means the next person does not need either of our theories. It also retires my "check kicad_list_footprints" complaint from the original report - a failure that answers the question itself is strictly better than a hint pointing at a verb to go call, existent or not.

The remaining failure

MachinePinLargeStandard, tja1051t-3-118 and nRF54L15 still do not load - deterministically, same three every run, while the other seven parts pass. The response no longer carries a library block for them, which I read as "the footprint IS in the library, so there is no near-miss to report" - i.e. the residual bug is purely the background load, not a lookup problem.

I am about to re-run the full 10-part x 4-surface matrix against 0.9.208 and will post the table here. If the new diagnostics say something specific about those three, that will be in it.

Agreed on the SDK point

A bridge that writes files owns pruning them. Worth putting in the SDK - I doubt this bridge is the only one that has been quietly growing a directory.

Agreed, and I will add it to BRIDGE_SDK.md as a rule rather than leaving it as folklore, crediting this issue for the concrete numbers. Your retention shape (age cap, hard file ceiling, oldest-first so an in-flight capture is never the one deleted, rate-limited so a burst does not pay for the sweep) is a better starting default than anything I would have invented, so I will document that shape specifically.

John Lauer ·

Item 2 splits: 1 of your 3 is a lookup problem, 2 are real. And I walked my theory back too far.

I ran the new listing against YOUR box before you re-run the matrix, because it answers this in one call:

kicad_list_footprints {library:"Adom"}   (AdomLapper, 22 footprints)

  MachinePinLargeStandard    exact=True     <- IS there
  nRF54L15                   exact=True     <- IS there
  tja1051t-3-118             exact=False    <- NOT there, not even case-insensitively

And the reason for the third is sitting in the listing:

SOIC127P600X175-8N

tja1051t-3-118 was installed under its in-file declared name. So my original hypothesis was right for that part, and my walk-back was too broad - I said the mismatch "cannot happen", when what is true is narrower: it cannot happen when the caller passes footprintName. My installer only derives the name from (footprint "...") when you pass none, so this part evidently went in without one. Corrected again, and this time with the listing rather than a story.

That is the same shape as the symbol problem you already solved on your side by using the returned symbolNames. kicad_install_footprint returns footprintName for exactly this reason - feeding that back into show_footprint makes the mismatch structurally impossible, and is the fix for this part.

So your three are actually two different bugs:

  1. tja1051t-3-118 - not a navigation failure at all. Requested name never existed in the library. On 0.9.208 this now says so outright, with existsInLibrary: false and closestMatches.
  2. MachinePinLargeStandard and nRF54L15 - both present, exact spelling, and still will not load. These are the real bug, and the 3/10 in your table is really 2/10.

On the missing library block: it is attached whenever the listing can be read, including when existsInLibrary is true - so on 0.9.208 those two should carry {existsInLibrary: true, libraryCount: 22}. If they come back with no block at all, the listing itself failed on that call and I want to know, because that is a third thing.

One data point that makes the remaining two stranger, not simpler: MachinePinLargeStandard loads fine on my conference-room box, first try, background path, ~9s - in a library that also contains MachinePinLargeShort, so a sibling-name collision is not the explanation. Same bridge, same KiCad version, different machine, opposite result. Your matrix output will be more informative than more guessing from me, so I will wait for it rather than offer a third theory.

On the screenshot ceiling

329 files / 60.8 MB against a 300 ceiling is the rate limit doing what it says - the sweep runs at most once per 10 minutes, and a 40-run matrix outpaces it between sweeps. It self-corrects on the next capture after the window.

But your number is the useful signal: 60.8 MB for 329 files means my captures are much bigger than the ones you originally measured (37.5 MB for 226), because evidence shots and stepShots are larger than the old state captures. A file-count ceiling was the wrong control for that - what you care about is megabytes. Adding a total-size cap alongside it so the directory cannot balloon regardless of file count. Will report the version.

John Lauer ·

0.9.209: megabyte cap shipped, and your 60.8 MB is already down

Your number was the right signal, and a file count was the wrong control. Evidence shots and stepShots are far bigger than the state captures you originally measured (60.8 MB / 329 files now, versus 37.5 MB / 226 then), so the same count costs almost double the disk. The directory now has a total-SIZE cap alongside the count, trimmed oldest-first:

age cap    48h
count cap  300 files
size cap   40 MB          <- new
rate limit once per 10 min

Verified against a synthetic 50-file / 95.4 MB tree: pruned to 20 files / 38.1 MB, newest kept, oldest removed. And on your box right after the install:

files=211 sizeMB=39.8      (was 329 / 60.8)

Oldest-first matters here for the reason you flagged in the SDK note: an in-flight capture must never be the one deleted, and the newest file is the one a caller is about to read.

If 40 MB turns out to be the wrong number for a heavy matrix day, say so and I will raise it - it is one constant. I would rather it be slightly too aggressive than have you find another quietly growing directory.

John Lauer ·

Closing - all three items re-verified on 0.9.217 just now

Re-ran each on an idle box rather than trusting the earlier runs:

Item 1 (P0), the false success:

editor on Adom:IN-S42ATR, then a focus-steal request that cannot load
-> success: false
   errorCode: wrong_footprint_on_screen
   "The keystroke path ran but the Footprint Editor is not showing 'NO-SUCH-FP-Z9'
    (title: Adom:IN-S42ATR — Footprint Editor)."

Both holes are closed: the keystroke tier now confirms the title, and the background tier no longer uses the substring test that certified a wrong part for symbols in 0.9.194.

Item 3, the hint that named a missing verb:

kicad_list_footprints {library:"Adom"} -> success: true, footprintCount: 11

Item 4, the unverifiable 3D view:

kicad_show_3d_chip {IN-S42ATR}
-> requested: IN-S42ATR | footprint: IN-S42ATR | verified: true

And it FAILS with wrong_part_in_3d_view when the source editor is on something else, rather than handing back an unverifiable success.

Item 2 is not dropped - it moved to #44, which is where the three deterministic failures are being worked. That one stays open: your file loads on my box, so it is environmental, and the tree-count check is the next step.

Closing this one because items 1, 3 and 4 are done and #44 carries the remainder with better evidence than this thread has.

John Lauer closed this ·

Log in to reply.