Troubleshooting & FAQ

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

My pod is not on the BenchPod page

A pod is only visible to the account (and organization) it is registered to. If it was set up for you, it is on the account your activation email was sent to: sign in with that address, not a new account. Google and GitHub sign-in reach the same account when they use that address. If the pod is on your network, benchpod discover says whether it is registered to the account this computer is signed in to (benchpod login).

benchpod register answers this bench pod is registered to an organization you are not in for the same reason. Sign in with the right account (benchpod login --force). If you are in that organization too, move the pod to the organization you want on its Settings tab (no need to register again). Otherwise have someone in that organization deregister the pod on their BenchPod page and register it again.

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. Install a newer one (the Linux and Windows tabs use the xPack OpenOCD 0.12.0-7 snapshot), or build a recent OpenOCD from source:

# Homebrew's stable open-ocd is 0.12.0, so build it from HEAD
brew install --HEAD open-ocd

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:<name>) or set BENCHPOD_CONNECTION to actually exercise the pod.

pytest never uses the connection the CLI saved (benchpod set-connection) on its own, even though BenchPod() in a script does. Pass --benchpod-connection=saved to use it; with nothing saved, the tests skip and say so.

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. The refusal names the fix for the tool you are using:

# Python SDK (pytest, scripts)
FirmwareError: uart_proxy_start: la voltage not set; set the board's I/O voltage first: BenchPod(conn, la_voltage=3.3) (1.8 for a 1.8 V target), the benchpod_la_voltage fixture in conftest.py, or bp.set_la_voltage(3.3)

# CLI
la voltage not set; run `benchpod la voltage 3.3V` (or 1.8V), or use the web app's Wiring tab

# MCP server (Claude, Codex)
FirmwareError: uart_proxy_start: la voltage not set; call set_la_voltage first with the DUT's I/O voltage (1.8 or 3.3)

In a test suite, set it once in conftest.py by overriding the benchpod_la_voltage fixture to return 3.3 (or 1.8 for a 1.8 V board); see the pytest docs. In a script, pass BenchPod(conn, la_voltage=3.3). A voltage set this way, or with the CLI or the plugin, lasts until the pod reboots, which is why a run can pass on one pod and fail on a freshly power-cycled one. The one saved in the web app (the workspace asks the first time you open a pod, or the Wiring tab) is applied again every time the pod connects to embeddedci.com.

Commands fail with “pin conflict: LA4 is in use by …”

Each LA channel has one owner at a time. The refusal says who holds the channel and how to free it:

# Python SDK
uart_proxy_start: pin conflict: LA4 is in use by gpio; release it with bp.release_gpio(4)

# MCP server
PinConflictError: uart_proxy_start: pin conflict: LA4 is in use by gpio; release it with gpio_release(la=[4])

The other owners: uart (close the UART session from bp.open_uart(), or uart_close), i2c (bp.disable_i2c_sensor(), disable_i2c_sensor), spi (close the bp.open_spi() session), gps (bp.disable_gps(), disable_gps) and swd (wait for the running flash). In the SDK, the pod's original message stays in err.firmware_message.

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 pod'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 computer sees no network adapter for the pod

The pod's USB network needs firmware 3.9.0 or later. On such firmware it is off while Wi-Fi is set up, and when its USB network setting is off: benchpod usb-network --connection usb shows which. The setting changes on the device only; turn it back on over the console with benchpod usb-network on --connection usb. The web app shows it, read-only, in the pod's Settings tab under Advanced. On a Mac, also check the screen was unlocked when the pod was plugged in.

A pod on USB works locally but is offline on embeddedci.com

Over USB-C alone the pod has no internet of its own: the USB network only links it to the computer it is plugged into. To bring it online for the web app, CI and your other computers, run the CLI's USB bridge on that computer (benchpod usb-bridge install, see Getting started); it carries only the pod's link to embeddedci.com. Or plug in Ethernet, the recommended link, and the pod gets online on its own.

A connection in UART mode carries only UART bytes, so it can't also send a power-on command. 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:<name> 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, which is 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 pod. See Security & trusted repos. A DeviceBusyError instead means another run holds the pod's lease; the SDK waits up to --benchpod-lease-wait seconds (default 600) for it to free.

What the pod's refusals mean

The pod checks every command before it runs it, and the SDK, CLI and web app show its message as is. The ones people meet most:

  • locked: … needs the cloud or USB (firmware before 3.9.0: … or the USB console): the pod's network access without sign-in is set to Instruments only, so settings and firmware updates over the network are refused. Use the cloud connection or USB, or set it back to Full in the pod's Settings tab (see Security).
  • busy: a cloud job holds this pod (…, N s left): a CI run, the web app or an SDK session holds the pod over the cloud; LAN clients can only read status until it ends. The name in the brackets says what: Pat's web workspace is the BenchPod page open in Pat's browser. It holds the pod only while Pat is using it (a connected terminal, or a click or key press in the last 45 seconds, with the tab visible), so it frees up on its own shortly after.
  • forbidden: … needs an organization owner or admin: settings and firmware updates need an owner or admin (and an API key needs the benchpod:admin scope).
  • busy on its own: another capture, upload or update is using the pod's capture memory. Retry when it ends.
  • safe mode: iCE40/PSRAM are off. Unplug and replug the pod: the pod crashed at boot twice in a row and came up with the FPGA and capture memory off, so captures, the DAC and the LA are refused. Network, status and target power still work. A power cycle clears it; if it comes back, send us the console's status output.
  • this BenchPod has no analog front end (digital board): the pod has no DAC or ADC fitted, so analog commands are refused. Logic-analyzer captures, GPIO, UART, SWD and CAN work.
  • capture data was overwritten by …; run the capture again: a resumed capture read-back came after something else (an update, an upload, another capture) reused that memory.
  • cloud_ca: change it from the cloud or over USB (or cloud_proxy; firmware before 3.9.0 says "or the USB console"), sig_policy: change it from the cloud or the USB console (or lan_policy): these settings can never be changed from Ethernet or Wi-Fi. From firmware 3.9.0 the company CA and the proxy also change over the pod's USB network adapter.

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 over Ethernet (recommended) or Wi-Fi: its IP. A pod on your desk with only a USB-C cable to your computer: usb (or usb:<name> with several pods), which reaches the full API over the pod's USB network adapter on firmware 3.9.0 and later, no Ethernet or Wi-Fi needed; a device path such as /dev/ttyACM0 is the console only. A pod somewhere else: embeddedci:<name>, with an API key from anywhere, or OIDC from GitHub Actions. The same test runs against all of them. 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.