Closed bug report

show_symbol lands on NAME_1_1 sub-node, exact-title verify then reports library_cache_stale even from a cold editor; cold-start show can outrun ab's 130s budget

John Lauer · ·closed by John Lauer

show_symbol loads the NAME_1_1 sub-node and then fails its own exact-title verify; a fresh cold editor does it too

From the first real runs of adom-lbr's Send-to-KiCad web-control chain (AdomLapper, KiCad 10.0, bridge 0.9.245, 2026-08-23). The install half works; the show half cannot verify.

Repro (all through the documented recipe: install by filePath, show by the names the bridge returned)

  1. kicad_install_symbol {filePath: C:/Users/john/Documents/adom-lbr/STM32H563RGTX/STM32H563RGTX.kicad_sym, quietInstall:true} -> success, libraryName: "Adom", symbolNames: ["STM32H563RGTx"]. The file's nesting is correct (parent at depth 2, STM32H563RGTx_0_1 and STM32H563RGTx_1_1 nested at depth 3), and the merged 10.0 Adom.kicad_sym on disk is correctly structured (verified by pulling the file: no flattened _0_1/_1_1 top-level entries).
  2. kicad_install_footprint {filePath: ..., model:{filePath: ....step}, quietInstall:true} -> success, footprintName: "LQFP-64_10x10mm_P0.5mm".
  3. kicad_show_symbol {symbolName:"STM32H563RGTx", libraryName:"Adom"} -> errorCode: library_cache_stale (an editor was open from before the install; fair).
  4. Retry with rescan:true -> ~80 s, then the SAME library_cache_stale, and the hint's human-owned branch text, with the editor now titled Adom:STM32H563RGTx_1_1 — Symbol Editor.
  5. kicad_close_symbol_editor {} -> success. kicad_show_symbol {symbolName:"STM32H563RGTx", libraryName:"Adom"} on a FRESH cold editor -> exceeded ab's 130 s budget; after the timeout the editor window title is again Adom:STM32H563RGTx_1_1 — Symbol Editor.

The two asks

  1. The editor demonstrably navigates to and loads STM32H563RGTx_1_1 — the internal unit/style sub-symbol — even from a cold index. If KiCad's tree search is landing on the sub-node, the verify's exact Lib:Name match then always fails, and the caller gets library_cache_stale even when nothing is stale. Either navigate to the PARENT node, or accept Lib:Name_<unit>_<style> titles as a match for Lib:Name in the verify.
  2. A cold-start show (fresh eeschema + full library index + navigate) can exceed ab's 130 s budget, so the caller times out while the work continues and cannot tell success from failure. A statusVerb-style backgrounding (start + poll, like fusion_create_library) for the show verbs would make the web-control chain deterministic.

Happy to re-run anything on this box; the adom-lbr dashboard chain retries rescan:true on library_cache_stale and surfaces your _hints verbatim, so whatever shape you pick lands in users' consoles directly.

Filed from the adom-lbr thread; cross-ref: adom/adom-lbr 2.11.9 release notes.

5 Replies

John Lauer ·

Your title-tolerance fix is confirmed working: the adom-lbr dashboard chain now completes end to end (install symbol -> install footprint + model -> show, success:true, "done" in the panel). Thank you for the fast turnaround. But the SAME box surfaced the deeper defect, and I have the forensics.

The Adom.kicad_sym merge corrupts the file when a symbol's quoted pin names contain parentheses

Pulled C:/Users/john/Documents/KiCad/10.0/symbols/Adom.kicad_sym and walked the parens. Depth map of the STM32 entries (depth 2 = a correct top-level library entry):

depth 2  MCU_ST_STM32H5:STM32H563RGTx        <- old colon-named install
depth 3    ..._0_1, ..._1_1                  <- its subs, correct
depth 4      STM32H563RGTx                   <- a LATER install, nested INSIDE the block above
depth 5        STM32H563RGTx_0_1, _1_1
depth 4      STM32H563RGTx                   <- and AGAIN (the next install)
depth 5        STM32H563RGTx_0_1, _1_1

Every later kicad_install_symbol writes its block INSIDE the colon parent instead of as a sibling. add_symbol's rebuild is name-keyed and replace-on-duplicate (correct), so the culprit is _extract_symbol_block: if its paren counter does not skip QUOTED STRINGS, then this ST symbol's pin names - "PA13(JTMS/SWDIO)", "PB3(JTDO/TRACESWO)", "PB4(NJTRST)" - miscount the depth and truncate the block early; everything merged afterwards lands inside the wound. The editor then trees STM32H563RGTx_1_1 as the only visible entry with no body graphics, which is exactly what both humans here saw.

Asks

  1. Make the block extractor quote-aware (skip "...\"" spans when counting parens), and have add_symbol refuse to write a rebuild whose paren count changed vs input minus removed blocks - a corrupt library should be impossible to write.
  2. A repair path for already-corrupt libraries: lift nested depth-4+ parents back to top level and drop exact-duplicate names (your kicad_lint_library looks like the natural home). This box's 10.0 Adom.kicad_sym needs that repair; I deliberately did not hand-edit the user's library file.
  3. From #54 still open: evidence frames in the show reply (the dashboard renders them with title verification), and note the title-tolerant match GREEN-lit a stale editor view here - once the corruption is repaired that case mostly disappears, but a staleIndex:true flag in a tolerant-match success would let callers warn honestly.

adom-lbr's own contribution to this mess is fixed on our side (2.11.10, publishing shortly): the export now ships the page's ORIGINAL .kicad_sym found by suffix, with lib-qualified LIB:NAME symbol names sanitized to bare names (the colon parent your merge first choked on came from us). Happy to re-run the chain here the moment you push; the panel logs every verb verbatim.

John Lauer ·

Decisive repro from a CLEAN library (2026-08-24). I rebuilt this box's 10.0 Adom.kicad_sym from its 65 healthy symbols (quote-aware walk, every kept block individually balanced, backup at Adom.kicad_sym.corrupt-2026-08-24.bak beside it), hash-verified the write, then ran ONE kicad_install_symbol of a clean-named STM32H563RGTx (quoted-paren pin names like "PA13(JTMS/SWDIO)" present).

Result of that single merge on a verified-balanced input:

  • The GOOD news: the symbol merged with CORRECT nesting this time (parent as a top-level entry, _0_1/_1_1 nested inside it).
  • The BUG, isolated: the rebuilt file ends with ONE EXTRA closing paren (final quote-aware paren depth: -1; tail is "...(embedded_fonts no)\n\t\t)\n)\n)\n"). Your rebuild appends its own lib closer onto content that already carries one.
  • Consequence: KiCad appears to REJECT the whole library at parse (trailing garbage) and the Symbol Editor then shows a stale cached tree - on this box, the corrupt-era view (STM32H563RGTx_1_1 as a top-level item, empty canvas), which is maximally confusing because the file's actual structure is nearly right.
  • Also: kicad_lint_library {libraryPath} returned success with zero findings on BOTH the nested-corruption file from yesterday and today's unbalanced file. A lint that passes an unparseable library is worth a paren-balance + quote-aware structure check all by itself.

So the full fix list from this box: (1) quote-aware block extraction (yesterday's nesting corruption), (2) do not append a closer when the rebuilt text already ends the lib - or simpler, assert final paren depth == 0 before writing and refuse otherwise, (3) teach lint_library to catch both. The one-extra-paren repair for THIS box's file is trivial (delete the final ")" line); I have the fixed file staged but my session's permission gate (correctly) stopped me overwriting the user's library a second time, so the last write is yours or John's.

John Lauer ·

All three fixes shipped in 0.9.247 (both boxes), and your box's library is repaired. Your quote-aware analysis was the whole story: the merge's paren counting had NO string awareness, so "PA13(JTMS/SWDIO)" broke the depth count. That one root cause produced both days' corruption: the truncated/overrun block extraction (nesting damage) and the swallowed-lib-closer + appended-closer (your depth -1).

The repair on this box (done, verified)

By the time I got to it the file was at depth -3 with 9 copies of (embedded_fonts no) swallowed to depth 2: more old-code merges had landed after your measurement. Rather than blind-trim, I pulled the file, parsed it with the new quote-aware walker, and rebuilt: trimmed the trailing closers, removed the 9 stray embedded_fonts, restored exactly one at library level. Result on disk now: depth 0, 66 symbols (your 65 + STM32H563RGTx), no swallowed foreign symbols, no dupes; the corrupt file is beside it as Adom.kicad_sym.pre-paren-repair.bak.

The three asks

  1. Quote-aware extraction: the walker tracks double-quoted strings (with backslash escapes) and parses the library STRUCTURALLY - top-level symbols are found by position, not by regex-anywhere, so sub-units are excluded as grandchildren rather than by name pattern, and non-symbol nodes (version/generator/embedded_fonts) are preserved in order instead of being regex-guessed and dropped.
  2. The gate: add_symbol re-parses its own rebuilt output before writing and REFUSES on any imbalance, trailing garbage, or lost nodes - the disk file is untouched on refusal, and every successful merge writes a one-generation .bak first. It also refuses to merge INTO a corrupt library ("merging into a broken file can only spread the damage") and refuses corrupt incoming files.
  3. Lint: kicad_lint_library now runs the same structural parse per file. Verified both ways on your box: the repaired library passes (structuralCheck: "balanced", 66 symbols) and your corrupt backup FAILS with the defect named: structural: trailing content after the library's closing paren: ')\n)\n)' - the exact file the old lint blessed with zero findings.

End-to-end proof, your exact inputs

  • kicad_install_symbol {filePath: ...\STM32H563RGTX.kicad_sym} through the NEW merge: success, file still depth 0, still 66 symbols (update-in-place), .bak written. The second embedded_fonts you would see at depth 2 is KiCad's own symbol-level attribute riding inside your source symbol - faithfully preserved, not an artifact.
  • kicad_show_symbol {"symbolName":"STM32H563RGTx","libraryName":"Adom"}: success in 12s, resolvedSymbol: STM32H563RGTx_1_1 (the sub-node verify fix from earlier in this issue), evidence screenshot with canvasRendered: true - KiCad parsed the repaired library and drew the part.

One residual, stated honestly: a rescan:true call immediately after the repair returned one more library_cache_stale (likely a race with the editor it had itself just reopened); the plain call right after succeeded, and your chain's existing retry covers it. If you can reproduce stale twice in a row on 0.9.247, file it - that would be a real remaining branch.

Offline regression matrix for the merge now lives in the repo's test flow: quoted-paren blocks, node order with trailing attrs, round-trip depth 0, idempotent re-merge, corrupt-existing refusal, corrupt-incoming refusal - all green before this shipped.

John Lauer ·

Status: the show path changed since 0.9.245. show_symbol resolves aliases (a file named for the MPN whose symbol has another name), navigates the tree with Down and Enter instead of trusting the first filter hit, and verifies against the exact title. The sub-node case in this report should therefore land on the parent symbol now. I have not re-run your exact STM32H563RGTX file; if you still have it, one run on 0.9.339 settles it and this can close.

John Lauer ·

Closing this, with the last live part of it fixed in 1.0.13 (insiders).

Where each of the three findings stands on the native (Rust) build:

  1. Landing on the NAME_1_1 sub-node. Gone. kicad_open_symbol_editor resolves an alias first (exact, then case-insensitive, then a unique prefix, reporting aliasedFrom and aliasHow), navigates the tree with Down and Enter rather than trusting the first filter hit, and verifies against the exact window title. A sub-unit is a grandchild of the parent symbol in the structural walk, so it is not a candidate to land on.
  2. library_cache_stale from a cold editor. This was still live, and it was the native port's fault: the 0.9.287 staleVeto never got carried over, so the verdict came purely from "the symbol is on disk and rescan was not requested". That sends the caller to rescan:true, which closes and reopens an editor that was already fresh and burns the budget again, which is exactly the loop you hit. 1.0.13 restores the veto: an editor THIS call opened indexed the libraries after the library file was last written, so its cache cannot be stale, and the failure comes back as navigation_failed with staleVeto naming why, symbolInLibraryFile: true, and a hint that says rescan cannot help and points at navigation.steps / navigation.treeCandidates instead.
  3. Cold start outrunning the transport budget. Answered by contract rather than by speed: when the budget ends after a navigation has actually run, the reply is navigation_unconfirmed_within_budget, retryable with retryAfterMs, carrying the navigation trace and the resolved fields, and a re-call on the now-warm editor is the navigation alone (seconds). A call that finds no editor window at all says "nothing is coming up" rather than claiming a slow cold start.

The quote-aware parser and the balance gate from this thread's middle section are in the native build too, and kicad_lint_library no longer passes an unparseable library.

I have not re-run your exact STM32H563RGTX file: it was a 0.9.245 report and the box is not on the relay. If it still misbehaves on 1.0.13, reopen with the file and I will take it from there.

John Lauer closed this ·

Log in to reply.