Troubleshooting & FAQ

The failures people hit most often with a BenchPod, and how to clear them.

Flashing fails: OpenOCD “lacks the cmsis-dap TCP backend”

FlashError: this OpenOCD (/usr/bin/openocd) lacks the cmsis-dap TCP backend; update
OpenOCD (e.g. `brew install --HEAD open-ocd`, or build a recent version with the
cmsis_dap_tcp backend)

Your OpenOCD is too old. Flashing drives the pod's CMSIS-DAP probe through OpenOCD's cmsis-dap adapter with its TCP backend (cmsis_dap_tcp), which only exists in builds newer than 0.12.0 — the stock packages (apt install openocd, brew install open-ocd) are 0.12.0 and lack it. On macOS use brew install --HEAD open-ocd; on Linux the xPack OpenOCD 0.12.0-7 snapshot has it, or build a recent OpenOCD from source. Verify the backend is there:

openocd -c "adapter driver cmsis-dap" -c "cmsis-dap backend tcp" \
        -c "cmsis-dap tcp port 4441" -c shutdown
# must exit cleanly — an error on the 'backend tcp' line means the build is too old

The SDK and the CLI's flash both run this check up front, so a too-old OpenOCD fails before anything touches the target.

My tests skip instead of running

That's by design. The benchpod fixture skips when no connection is configured, so a suite stays green on runners without hardware. Pass --benchpod-connection=... (an IP, a USB port, or embeddedci:<device>) or set BENCHPOD_CONNECTION to actually exercise the pod.

Commands fail with “la voltage not set”

The pod's LA I/O bank has no default voltage, so it refuses flashing, UART, logic capture, pull resistors and I2C-sensor emulation until one is chosen. Match it to your DUT once, in conftest.py, by overriding the benchpod_la_voltage fixture to return 3.3 (or 1.8 for a 1V8 board) — see the pytest docs. In a script, pass BenchPod(conn, la_voltage=3.3). The setting lasts until the pod reboots, which is why a run can pass on one pod and fail on a freshly power-cycled one.

The wiring profile won't save: “LA4 is used by both uart_tx and i2c_sda”

Two roles or named signals share an LA channel in the bench's wiring profile, and it is rejected until every channel has one user. A key you never set still takes its default — UART RX LA5 and TX LA4, I2C SDA LA1 and SCL LA2, SWCLK LA11 and SWDIO LA12 — so moving I2C SDA onto LA4 collides with the default UART TX unless you move the UART or mark it not wired. The web app's Wiring tab highlights both users on its channel map.

Flash can't reach the target (connect-under-reset)

If the read fails on connect, NRST is the usual culprit — a floating or mis-wired reset line blocks connect-under-reset. The pod drives reset from its own pin (DUT header J1 pin 22), sonreset=True only means "the target's reset is wired to it". If your firmware doesn't remap the SWD pins, NRST isn't required: retry the flash with nreset=False. The SDK's flash(..., check=False) returns a result you can inspect (result.target_unreachable) to fall back automatically.

Flashing is flaky or intermittent

  • Lower the adapter speed — long or noisy SWD wiring doesn't clock reliably at the default.
  • Keep SWCLK/SWDIO leads short, and make sure grounds are solid between pod and DUT.
  • Confirm the target is actually powered (use the eFuse target_power, not just bench power).

The USB link drops mid-capture

The pod handles one connection at a time — you can't send a command while the UART proxy owns the link. That's why the API schedules a power-on pod-side and then opens the capture, so the boot output lands in an already-listening window. Use power_on(efuse, delay=...) + open_uart(...), or the one-shot power_cycle_and_capture(...) — see Catching Boot Regressions.

Cloud: “cannot mint a GitHub OIDC token”

The embeddedci:<device> connection authenticates with an API key when one is set (BENCHPOD_API_KEY / --benchpod-api-key), and otherwise with a GitHub OIDC token. When the OIDC token can't be minted, the error names the cause — one of:

  • Not running inside a GitHub Action — OIDC only exists in GitHub Actions. Anywhere else, set BENCHPOD_API_KEY to an API key with the benchpod:control scope.
  • Missing id-token: write — add it to the job's permissions.
  • Token request failed — a transient GitHub/network issue; retry.

Also confirm the repo is trusted and allowed to drive that device — see Security & trusted repos. A DeviceBusyError instead means another run holds the device's lease; the SDK waits up to --benchpod-lease-wait seconds (default 600) for it to free.

FAQ

Which boards are supported?

Anything OpenOCD can flash over SWD. The target= argument is a normal OpenOCD config (target/stm32f4x.cfg, target/stm32h7x.cfg, target/nrf52.cfg, …), so changing chips is a one-line change.

Network, USB, or cloud — which connection do I use?

A pod on your LAN: its IP. A pod on your desk over USB: a device path or usb. A pod somewhere else: embeddedci:<device-name> — with an API key from anywhere, or OIDC from GitHub Actions. The same test runs against all three. See the connection strings.

Does my whole suite need hardware?

No — mark hardware tests with @pytest.mark.hardware; without a connection they skip, so fast software tests still run everywhere.