What happened
An install completed end-to-end successfully — build succeeded, OTA succeeded, the new firmware is on the device and running — but the dashboard's Install dialog shows a red "Install failed" banner.
Discord report: https://discord.com/channels/429907082951524364/1506842338750628020
The log tail makes it obvious that the underlying ESPHome process succeeded:
[SUCCESS] Took 260.63 seconds
INFO Build Info: config_hash=0xa26c8852 build_time_str=2026-05-20 20:51:43 -0500
INFO Successfully compiled program.
INFO Connecting to 2603:8081:1607:f280:8a56:a6ff:fe6e:4820 port 3232 ...
INFO Connected to 2603:8081:1607:f280:8a56:a6ff:fe6e:4820
Uploading: [============================================================] 100% Done...
INFO Upload took 10.99 seconds, waiting for result...
INFO OTA successful
INFO Successfully uploaded program.
(Full log attached below.)
Root cause
controllers/firmware/runner.py:176 requires both exit_code == 0 and not has_error_in_output:
success = exit_code == 0 and not has_error_in_output
has_error_in_output is set when any output line matches a substring in _ERROR_PATTERNS (controllers/firmware/constants.py:30):
https://github.com/esphome/device-builder/blob/main/esphome_device_builder/controllers/firmware/constants.py#L30-L36
_ERROR_PATTERNS = [
"ModuleNotFoundError",
"ImportError",
"No module named", # <-- too loose
"FileNotFoundError",
"command not found",
]
This build's PlatformIO output contains:
[nanopb] Installing Protocol Buffers dependencies
/root/.platformio/penv/bin/python: No module named pip
[nanopb] Installing gRPC dependencies
/root/.platformio/penv/bin/python: No module named pip
[nanopb] No generation needed.
PlatformIO's [nanopb] extra-script tries to bootstrap pip inside the PIO venv (which doesn't ship pip), fails harmlessly, and proceeds. The next line is [nanopb] No generation needed. — generation already done from cache. The build is fine.
But the bare substring "No module named" matches, has_error_in_output flips True, and even though exit_code == 0 and the OTA logs success, the dashboard reports failure.
Suggested fix
Drop the bare substring. The remaining patterns (ModuleNotFoundError, ImportError, FileNotFoundError, command not found) already cover Python tracebacks that surface real failures, and the specific _NO_ESPHOME_MODULE_MARKER = "No module named 'esphome'" at constants.py:42 still handles the saw_no_esphome_module branch that produces the actionable error message:
_ERROR_PATTERNS = [
"ModuleNotFoundError",
"ImportError",
"FileNotFoundError",
"command not found",
]
A narrower alternative (keep the substring but anchor on the quoted form CPython actually prints) would also work:
_ERROR_PATTERNS = [
...,
"No module named '", # CPython prints quoted module names; PlatformIO's "python: No module named pip" doesn't match
]
Reproduction
Any device config that exercises the [nanopb] extra-script via a PIO library that pulls it in (the reporter's config uses noise-c via the TeslaBLE component). Install via the dashboard. PIO logs the four No module named pip lines; dashboard ends with the red banner despite a successful OTA.
Logs
Full tesla-ble-6e4820 install log (tail)
Reading CMake configuration...
[nanopb] Installing Protocol Buffers dependencies
/root/.platformio/penv/bin/python: No module named pip
[nanopb] Installing gRPC dependencies
/root/.platformio/penv/bin/python: No module named pip
[nanopb] No generation needed.
Generating assembly for certificate bundle...
[nanopb] Installing Protocol Buffers dependencies
/root/.platformio/penv/bin/python: No module named pip
[nanopb] Installing gRPC dependencies
/root/.platformio/penv/bin/python: No module named pip
[nanopb] No generation needed.
Dependency Graph
|-- TeslaBLE @ 2026.5.0+sha.e63742c
|-- noise-c @ 0.1.11
... (compile + link, all clean) ...
RAM: [== ] 18.1% (used 59212 bytes from 327680 bytes)
Flash: [======== ] 79.9% (used 1465986 bytes from 1835008 bytes)
=========================== [SUCCESS] Took 260.63 seconds ===========================
INFO Build Info: config_hash=0xa26c8852 build_time_str=2026-05-20 20:51:43 -0500
INFO Successfully compiled program.
INFO Connecting to 2603:8081:1607:f280:8a56:a6ff:fe6e:4820 port 3232 ...
INFO Connected to 2603:8081:1607:f280:8a56:a6ff:fe6e:4820
Uploading: [============================================================] 100% Done...
INFO Upload took 10.99 seconds, waiting for result...
INFO OTA successful
INFO Successfully uploaded program.
Workaround for users
The firmware is installed; the banner is wrong. Confirm the device is online and running the new firmware (the device card on the dashboard or HA will show it). No action needed beyond closing the dialog.
What happened
An install completed end-to-end successfully — build succeeded, OTA succeeded, the new firmware is on the device and running — but the dashboard's Install dialog shows a red "Install failed" banner.
Discord report: https://discord.com/channels/429907082951524364/1506842338750628020
The log tail makes it obvious that the underlying ESPHome process succeeded:
(Full log attached below.)
Root cause
controllers/firmware/runner.py:176requires bothexit_code == 0andnot has_error_in_output:has_error_in_outputis set when any output line matches a substring in_ERROR_PATTERNS(controllers/firmware/constants.py:30):https://github.com/esphome/device-builder/blob/main/esphome_device_builder/controllers/firmware/constants.py#L30-L36
This build's PlatformIO output contains:
PlatformIO's
[nanopb]extra-script tries to bootstrappipinside the PIO venv (which doesn't ship pip), fails harmlessly, and proceeds. The next line is[nanopb] No generation needed.— generation already done from cache. The build is fine.But the bare substring
"No module named"matches,has_error_in_outputflips True, and even thoughexit_code == 0and the OTA logs success, the dashboard reports failure.Suggested fix
Drop the bare substring. The remaining patterns (
ModuleNotFoundError,ImportError,FileNotFoundError,command not found) already cover Python tracebacks that surface real failures, and the specific_NO_ESPHOME_MODULE_MARKER = "No module named 'esphome'"at constants.py:42 still handles thesaw_no_esphome_modulebranch that produces the actionable error message:A narrower alternative (keep the substring but anchor on the quoted form CPython actually prints) would also work:
Reproduction
Any device config that exercises the
[nanopb]extra-script via a PIO library that pulls it in (the reporter's config usesnoise-cvia the TeslaBLE component). Install via the dashboard. PIO logs the fourNo module named piplines; dashboard ends with the red banner despite a successful OTA.Logs
Full
tesla-ble-6e4820install log (tail)Workaround for users
The firmware is installed; the banner is wrong. Confirm the device is online and running the new firmware (the device card on the dashboard or HA will show it). No action needed beyond closing the dialog.