Electrical Rules Check (ERC) User Guide

Complete guide to using ERC validation in kicad-sch-api.

What is ERC?

Electrical Rules Check (ERC) automatically validates your schematic for common electrical errors like:

  • βœ… Output pins shorted together

  • βœ… Power supply conflicts

  • βœ… Dangling wires

  • βœ… Duplicate component references

  • βœ… Missing component values

Quick Start

Basic Usage

import kicad_sch_api as ksa
from kicad_sch_api.validation import ElectricalRulesChecker

# Load your schematic
sch = ksa.load_schematic("my_circuit.kicad_sch")

# Run ERC
erc = ElectricalRulesChecker(sch)
result = erc.run_all_checks()

# Check results
if result.has_errors():
    print(f"Found {len(result.errors)} errors!")
    for error in result.errors:
        print(f"  - {error.message}")
else:
    print("No errors found!")

# Print summary
print(result.summary())  # "0 errors, 3 warnings"

Understanding Results

The ERC returns an ERCResult object with three categories:

# Errors - must be fixed
for error in result.errors:
    print(f"ERROR [{error.error_code}]: {error.message}")
    print(f"  Components: {', '.join(error.component_refs)}")

# Warnings - should be reviewed
for warning in result.warnings:
    print(f"WARNING [{warning.error_code}]: {warning.message}")

# Info - informational messages
for info in result.info:
    print(f"INFO: {info.message}")

Common Errors

E001: Pin Conflict

Problem: Two output pins connected together

# Bad: Two outputs driving same net
sch = ksa.create_schematic("Test")
u1 = sch.components.add("Device:R", "U1", "1k", (100, 100))
u2 = sch.components.add("Device:R", "U2", "1k", (150, 100))
sch.add_wire_between_pins("U1", "2", "U2", "2")  # Both outputs!

# ERC will report: "Pin conflict: output (U1) connected to output (U2)"

Solution: Use a buffer or remove one output

E004: Duplicate Reference

Problem: Same reference used twice

# Bad: R1 used twice
r1a = sch.components.add("Device:R", "R1", "1k", (100, 100))
r1b = sch.components.add("Device:R", "R1", "2k", (150, 100))  # ERROR!

# ERC will report: "Duplicate reference designator: R1"

Solution: Rename one component

r1 = sch.components.add("Device:R", "R1", "1k", (100, 100))
r2 = sch.components.add("Device:R", "R2", "2k", (150, 100))  # Fixed

W002: Dangling Wire

Problem: Wire with only one connection

# Wire goes nowhere
pin_pos = sch.get_component_pin_position("R1", "1")
sch.wires.add(start=pin_pos, end=(pin_pos.x + 20, pin_pos.y))  # WARNING

# ERC will report: "Wire has unconnected endpoint"

Solution: Connect wire or remove it

W008: Missing Value

Problem: Component has no value

# Bad: No value specified
r1 = sch.components.add("Device:R", "R1", "", (100, 100))

# ERC will report: "Component R1 has no value"

Solution: Add a value

r1 = sch.components.add("Device:R", "R1", "10k", (100, 100))

Configuration

Changing Severity Levels

Make specific warnings into errors (or vice versa):

from kicad_sch_api.validation import ERCConfig

config = ERCConfig()

# Make missing values an error instead of warning
config.set_severity("missing_value", "error")

# Make unconnected inputs just info instead of warning
config.set_severity("unconnected_input", "info")

# Use custom config
erc = ElectricalRulesChecker(sch, config=config)
result = erc.run_all_checks()

Suppressing Warnings

Suppress specific warnings you know are okay:

config = ERCConfig()

# Suppress all W001 warnings globally
config.suppress_warning("W001")

# Suppress W002 only for component R1
config.suppress_warning("W002", component="R1")

erc = ElectricalRulesChecker(sch, config=config)
result = erc.run_all_checks()

Custom Pin Conflict Rules

Override default pin conflict matrix:

from kicad_sch_api.validation import PinConflictMatrix, PinSeverity

# Create custom matrix
matrix = PinConflictMatrix()

# Allow Output-to-Output (downgrade from ERROR to WARNING)
matrix.set_rule("output", "output", PinSeverity.WARNING)

# Use with validator
from kicad_sch_api.validation.validators import PinTypeValidator

pin_validator = PinTypeValidator(sch, pin_matrix=matrix)
violations = pin_validator.validate()

Exporting Results

JSON Export

# Get results as JSON
json_output = result.to_json()
print(json_output)

# Save to file
with open("erc_report.json", "w") as f:
    f.write(json_output)

Dictionary Export

# Get as Python dict
data = result.to_dict()

# Access fields
print(f"Total checks: {data['total_checks']}")
print(f"Passed: {data['passed_checks']}")
print(f"Duration: {data['duration_ms']}ms")

for error in data['errors']:
    print(f"{error['error_code']}: {error['message']}")

Text Report

# Print detailed text report
print(f"ERC Results: {result.summary()}")
print(f"Checks: {result.passed_checks}/{result.total_checks} passed")
print(f"Duration: {result.duration_ms:.1f}ms")
print()

if result.errors:
    print("ERRORS:")
    for error in result.errors:
        print(f"  [{error.error_code}] {error.message}")
        if error.component_refs:
            print(f"    Components: {', '.join(error.component_refs)}")
        if error.suggested_fix:
            print(f"    Fix: {error.suggested_fix}")
        print()

if result.warnings:
    print("WARNINGS:")
    for warning in result.warnings:
        print(f"  [{warning.error_code}] {warning.message}")
        if warning.component_refs:
            print(f"    Components: {', '.join(warning.component_refs)}")
        print()

Filtering Results

By Severity

# Get only errors
errors = result.filter_by_severity("error")

# Get only warnings
warnings = result.filter_by_severity("warning")

By Component

# Get all violations affecting R1
r1_violations = result.filter_by_component("R1")

for violation in r1_violations:
    print(f"{violation.severity}: {violation.message}")

Running Specific Checks

Run only certain validation categories:

erc = ElectricalRulesChecker(sch)

# Run only pin type checks
pin_violations = erc.run_check("pin_types")

# Run only connectivity checks
conn_violations = erc.run_check("connectivity")

# Run only component checks
comp_violations = erc.run_check("components")

# Run only power checks
power_violations = erc.run_check("power")

Integration Examples

CI/CD Pipeline

#!/usr/bin/env python3
import sys
import kicad_sch_api as ksa
from kicad_sch_api.validation import ElectricalRulesChecker

def validate_schematic(filename):
    """Validate schematic for CI/CD."""
    sch = ksa.load_schematic(filename)
    erc = ElectricalRulesChecker(sch)
    result = erc.run_all_checks()

    # Print results
    print(result.summary())

    # Save JSON report
    with open("erc_report.json", "w") as f:
        f.write(result.to_json())

    # Exit with error if violations found
    if result.has_errors():
        print("ERC failed!")
        for error in result.errors:
            print(f"  ERROR: {error.message}")
        sys.exit(1)
    else:
        print("ERC passed!")
        sys.exit(0)

if __name__ == "__main__":
    validate_schematic(sys.argv[1])

Batch Validation

import glob
import kicad_sch_api as ksa
from kicad_sch_api.validation import ElectricalRulesChecker

# Validate all schematics in directory
for filename in glob.glob("schematics/*.kicad_sch"):
    print(f"Validating {filename}...")

    sch = ksa.load_schematic(filename)
    erc = ElectricalRulesChecker(sch)
    result = erc.run_all_checks()

    if result.has_errors():
        print(f"  ❌ {result.summary()}")
    else:
        print(f"  βœ… {result.summary()}")

Pre-Save Validation

def create_and_validate_circuit():
    """Create circuit with automatic validation."""
    sch = ksa.create_schematic("My Circuit")

    # Add components
    r1 = sch.components.add("Device:R", "R1", "1k", (100, 100))
    led = sch.components.add("Device:LED", "D1", "RED", (150, 100))

    # Connect
    sch.add_wire_between_pins("R1", "2", "D1", "1")

    # Validate before saving
    erc = ElectricalRulesChecker(sch)
    result = erc.run_all_checks()

    if result.has_errors():
        raise ValueError(f"ERC failed: {result.summary()}")

    # Save only if validation passes
    sch.save("my_circuit.kicad_sch")
    print("Circuit saved successfully!")

Error Code Reference

Errors (E-xxx)

Code

Description

Severity

E001

Pin conflict (Output-Output)

ERROR

E002

Power output conflict

ERROR

E003

Output to power rail

ERROR

E004

Duplicate reference

ERROR

E005

Invalid reference format

ERROR

Warnings (W-xxx)

Code

Description

Severity

W001

Unconnected input pin

WARNING

W002

Dangling wire

WARNING

W003

Undriven net

WARNING

W004

Missing power flag

WARNING

W005

Unspecified pin type

WARNING

W007

Missing footprint

WARNING

W008

Missing value

WARNING

Best Practices

1. Run ERC Early and Often

# After each major change
erc = ElectricalRulesChecker(sch)
result = erc.run_all_checks()
assert not result.has_errors(), "Fix errors before continuing"

2. Use Custom Config for Your Standards

# Create company/project config
company_config = ERCConfig()
company_config.set_severity("missing_footprint", "error")  # Stricter
company_config.set_severity("unconnected_input", "info")   # More relaxed

# Reuse across projects
erc = ElectricalRulesChecker(sch, config=company_config)

3. Document Suppressed Warnings

# If you suppress a warning, document why
config = ERCConfig()

# R1 intentionally has no connection (test point)
config.suppress_warning("W001", component="R1")

# Document in code or schematic

4. Integrate with Version Control

# Add ERC check to git pre-commit hook
#!/bin/bash
python3 scripts/run_erc.py my_circuit.kicad_sch || exit 1

Performance

ERC is designed to be fast:

  • Small schematics (<50 components): <10ms

  • Medium schematics (50-100 components): <100ms

  • Large schematics (100-1000 components): <500ms

To check performance:

result = erc.run_all_checks()
print(f"ERC completed in {result.duration_ms:.1f}ms")

Troubleshooting

β€œNo violations found but I see errors”

Some checks require full net tracing (coming in future update):

  • Advanced dangling wire detection

  • Undriven net detection

  • Complex power validation

β€œToo many false positives”

Use configuration to tune sensitivity:

config = ERCConfig()
config.suppress_warning("W001")  # Suppress unconnected inputs
config.set_severity("missing_value", "info")  # Downgrade to info

β€œCustom components not validated”

Ensure your KiCAD symbol libraries are installed and accessible. ERC reads pin types from actual symbol definitions.

Further Reading

Questions?


Happy circuit validation! ⚑