fix: ERC handler fails on KiCad 9 schematics

Two issues with run_erc on KiCad 9:

1. kicad-cli returns non-zero exit code when ERC violations exist.
   The handler treated this as a command failure and returned early
   with success=false, even though valid JSON output was produced.
   Fix: check for output file existence instead of exit code.

2. KiCad 9 nests violations under sheets[].violations instead of
   (or in addition to) the top-level violations[] array used by
   KiCad 8. The handler only read the top-level array, reporting
   0 violations on schematics with sub-sheets.
   Fix: iterate sheets[] and collect all nested violations.

Both fixes are backward-compatible with KiCad 8.

6 unit tests added covering non-zero exit codes, KiCad 8 top-level
violations, KiCad 9 sheets[] nesting, mixed structures, zero
violations, and missing output files.
This commit is contained in:
Leah Armstrong
2026-04-14 12:52:17 -04:00
parent eea43d18f1
commit 15d06e449a
2 changed files with 262 additions and 4 deletions

View File

@@ -2664,11 +2664,14 @@ class KiCADInterface:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
if result.returncode != 0:
logger.error(f"ERC command failed: {result.stderr}")
# kicad-cli returns non-zero when ERC violations are found —
# this is normal, not an error. Only fail when no JSON was
# produced (genuine CLI failure).
if not os.path.exists(json_output) or os.path.getsize(json_output) == 0:
logger.error(f"ERC command produced no output: {result.stderr}")
return {
"success": False,
"message": "ERC command failed",
"message": "ERC command failed - no output produced",
"errorDetails": result.stderr,
}
@@ -2678,7 +2681,14 @@ class KiCADInterface:
violations = []
severity_counts = {"error": 0, "warning": 0, "info": 0}
for v in erc_data.get("violations", []):
# KiCad 9 nests violations under sheets[].violations
# instead of (or in addition to) the top-level violations
# array used by KiCad 8.
all_violations = erc_data.get("violations", [])
for sheet in erc_data.get("sheets", []):
all_violations.extend(sheet.get("violations", []))
for v in all_violations:
vseverity = v.get("severity", "error")
items = v.get("items", [])
loc = {}