Skip to content

Error Handling

openocd-python uses a structured exception hierarchy rooted at OpenOCDError. Every exception the library raises is a subclass of this base, so you can catch broadly or narrowly depending on your needs.

OpenOCDError
├── ConnectionError # TCP connection failures
├── TimeoutError # Deadline exceeded
├── TargetError # Target not responding or returned an error
│ └── TargetNotHaltedError # Operation requires halted target
├── FlashError # Flash operation failed
├── JTAGError # JTAG communication error
├── SVDError # SVD file or parsing error
├── ProcessError # OpenOCD subprocess failed
└── BreakpointError # Breakpoint/watchpoint operation failed

All exceptions are importable from the top-level openocd package:

from openocd import (
OpenOCDError,
ConnectionError,
TimeoutError,
TargetError,
TargetNotHaltedError,
FlashError,
JTAGError,
SVDError,
ProcessError,
)

The base class for all library exceptions. Catching this handles any error that openocd-python can raise:

from openocd import OpenOCDError, Session
with Session.connect_sync() as ocd:
try:
state = ocd.target.state()
except OpenOCDError as e:
print(f"Something went wrong: {e}")

Raised when a TCP connection to OpenOCD cannot be established. Common causes:

  • OpenOCD is not running
  • Wrong host or port
  • Firewall blocking the connection
  • Network unreachable
from openocd import ConnectionError, Session
try:
with Session.connect_sync(host="192.168.1.99", port=6666) as ocd:
ocd.target.state()
except ConnectionError as e:
print(f"Cannot reach OpenOCD: {e}")

Also raised if a command is sent after the connection has been closed, or if OpenOCD closes the connection unexpectedly during a read.

Raised when an operation exceeds its deadline. This can happen during:

  • Initial connection (Session.connect() with timeout parameter)
  • Waiting for OpenOCD to start (Session.start() with timeout parameter)
  • Individual command responses (the TclRpcConnection timeout)
  • target.wait_halt() when the target does not halt in time
from openocd import TimeoutError, Session
with Session.connect_sync() as ocd:
try:
# Wait up to 2 seconds for the target to halt
ocd.target.wait_halt(timeout_ms=2000)
except TimeoutError:
print("Target did not halt within 2 seconds")

Raised when a target command fails. The error message contains the raw OpenOCD response for diagnosis. This covers halt, resume, step, reset, memory read/write, and register access failures.

from openocd import TargetError, Session
with Session.connect_sync() as ocd:
try:
ocd.target.halt()
except TargetError as e:
print(f"Target command failed: {e}")

A subclass of TargetError, raised specifically when an operation requires a halted target but the target is running. This is most commonly encountered when reading or writing registers.

from openocd import TargetNotHaltedError, Session
with Session.connect_sync() as ocd:
try:
pc = ocd.registers.pc()
except TargetNotHaltedError:
print("Halt the target first before reading registers")
ocd.target.halt()
pc = ocd.registers.pc()

Because TargetNotHaltedError is a subclass of TargetError, catching TargetError will also catch it:

try:
pc = ocd.registers.pc()
except TargetNotHaltedError:
# Handle the specific case
ocd.target.halt()
pc = ocd.registers.pc()
except TargetError:
# Handle other target errors
print("Unexpected target error")

Raised when a flash operation fails — programming, erasing, verifying, or reading flash memory. The error message includes the raw OpenOCD response.

from openocd import FlashError, Session
from pathlib import Path
with Session.connect_sync() as ocd:
try:
ocd.flash.write_image(Path("firmware.bin"))
except FlashError as e:
print(f"Flash programming failed: {e}")

Also raised for:

  • Invalid sector ranges (e.g., first > last in erase_sector)
  • Verification mismatches after write_image with verify=True
  • Unparseable flash info output

Raised when JTAG communication fails. This covers chain scan errors, TAP state transitions, and raw scan operations.

from openocd import JTAGError, Session
with Session.connect_sync() as ocd:
try:
taps = ocd.jtag.scan_chain()
except JTAGError as e:
print(f"JTAG error: {e}")

Raised when SVD-related operations fail:

  • SVD file not found or cannot be parsed
  • Peripheral name not found in the loaded SVD
  • Register name not found within a peripheral
  • No SVD file loaded when trying to list or decode
from openocd import SVDError, Session
from pathlib import Path
with Session.connect_sync() as ocd:
try:
ocd.svd.load(Path("nonexistent.svd"))
except SVDError as e:
print(f"SVD error: {e}")

Raised when the OpenOCD subprocess fails to start or exits unexpectedly. This only applies when using Session.start().

from openocd import ProcessError, Session
try:
with Session.start_sync("nonexistent_config.cfg") as ocd:
pass
except ProcessError as e:
print(f"OpenOCD failed to start: {e}")

Common causes:

  • OpenOCD binary not found on PATH
  • Invalid configuration file
  • OpenOCD exits before the TCL RPC port is ready
  • Permission errors

Raised when a breakpoint or watchpoint operation fails. Defined in openocd.breakpoints but inherits from OpenOCDError.

from openocd.breakpoints import BreakpointError
from openocd import Session
with Session.connect_sync() as ocd:
try:
ocd.breakpoints.add(0x08001234, length=2, hw=True)
except BreakpointError as e:
print(f"Breakpoint error: {e}")
from openocd import OpenOCDError, Session
with Session.connect_sync() as ocd:
try:
ocd.target.halt()
pc = ocd.registers.pc()
data = ocd.memory.read_u32(pc, count=4)
except OpenOCDError as e:
print(f"Operation failed: {e}")

Narrow catch — handle specific failure modes

Section titled “Narrow catch — handle specific failure modes”
from openocd import (
Session,
ConnectionError,
TargetError,
TargetNotHaltedError,
TimeoutError,
)
try:
with Session.connect_sync() as ocd:
ocd.target.halt()
pc = ocd.registers.pc()
ocd.target.resume()
except ConnectionError:
print("Could not connect to OpenOCD")
except TargetNotHaltedError:
print("Target is not halted")
except TimeoutError:
print("Operation timed out")
except TargetError as e:
print(f"Target error: {e}")
from openocd import Session, TimeoutError
with Session.connect_sync() as ocd:
for attempt in range(3):
try:
ocd.target.halt()
ocd.target.wait_halt(timeout_ms=1000)
break
except TimeoutError:
if attempt == 2:
raise
print(f"Attempt {attempt + 1} timed out, retrying...")
ocd.target.reset(mode="halt")

A common pattern is to attempt a register read and automatically halt if needed:

from openocd import Session, TargetNotHaltedError
def safe_read_pc(ocd) -> int:
try:
return ocd.registers.pc()
except TargetNotHaltedError:
ocd.target.halt()
return ocd.registers.pc()
with Session.connect_sync() as ocd:
pc = safe_read_pc(ocd)
print(f"PC = 0x{pc:08X}")

Internally, most subsystems detect errors by checking for the word “error” in the OpenOCD response string. This is because OpenOCD’s TCL RPC protocol does not use structured error codes — all errors are communicated as plain text in the response body.

The library wraps these text responses in the appropriate exception type so you do not need to parse them yourself. The original OpenOCD message is preserved in the exception’s string representation.