Each entry below gives a symptom, its likely cause and the fix. Two habits prevent most trouble:
- Look at the events. The ESP reports every boot step on the USB event channel:
BOOT ...,HEALTH ...,FIRMWARE ...,MOS: ...,ERR .... Install logs,tools/vios_coldboot_capture.pyand the harness log (docs/bring-up/vios-harness.jsonl) all show them. - Take a snapshot before anything risky. Use
:snapshot FILEin the CLI, or thesnapshottool. A reset loses RAM work. Only saved modules in the confirmed boot bundle come back.
The board is not found, or the wrong port
Symptom: a tool fails with "No such file or directory", "could not open port", or connect times out.
Cause: the USB serial suffix changes with the USB socket and hub. Several tools still default to an old suffix, /dev/cu.usbserial-224130.
Fix:
ls /dev/cu.usbserial-*
Pass the device you see to every tool with --port, for example --port /dev/cu.usbserial-210. If nothing is listed:
- check that the cable carries data (some USB-C cables are charge-only);
- try another socket or skip the hub;
- check that macOS sees a CH340 device (System Information, USB).
The CLI is different: its --port is the harness's TCP port (8765), not a serial device.
"Address already in use", or the port is locked
Symptom: the harness exits with Address already in use on 127.0.0.1:8765. Or connect, esptool, vios_firmware_ab.py or vios_sd_refresh.py fails because the serial port is busy or cannot be locked exclusively.
Cause: another harness, terminal or tool owns the port or the TCP socket. Only one process can own the serial port.
Fix: find the owner, then reuse it or stop it:
lsof -nP -iTCP:8765 -sTCP:LISTEN
lsof /dev/cu.usbserial-210
If a harness is already running, connect the CLI or Claude to it instead of starting another. Before using a tool that opens the port itself (the firmware installer, the SD refresher, the older acceptance drivers), snapshot your work and stop the harness with Ctrl+C.
Every harness start reboots the board
Symptom: RAM definitions are gone after you restart the harness or run an installer.
Cause: on this board, opening the USB serial port resets it. This is expected.
Fix: keep one harness running and attach clients to it. Save work as modules (see Modules and Persistence) or put startup code in /VIOS/USER.FTH.
The screen stays black
Cause and fix:
- No VGA signal: check the cable and that the monitor accepts 640×480 at 60 Hz. The desktop appears within about two seconds of power-on, before the eZ80 has loaded.
- The ESP firmware is not running. Read the boot events from the Mac:
vios_firmware_ab.py --port ... statusshows which slot boots. If the slot holds something other than VIOS, install the firmware again (see Installing).
Boot stops at the MOS prompt
Symptom: the Console window shows the MOS prompt, or one of these messages:
| Message | Cause | Fix |
|---|---|---|
| "VIOS: no boot SD card, or no viosboot on it." | No card, the card is unreadable, or its boot file does not run viosboot. |
Insert the card. Check it on the Mac with vios_sd_install.py check /Volumes/AGON, and run install if needed. Remember that MOS 3 runs only the first of !boot.obey, autoexec.obey, autoexec.txt that exists. |
VIOSBOOT: no ESP image, trying /vios/vios.bin |
The firmware's embedded resident failed its CRC check or did not answer. The stub falls back to the SD copy. | If the boot then succeeds, a firmware on trial rolls itself back; otherwise reinstall the firmware. |
VIOSBOOT: no bootable VIOS image; staying in MOS |
Neither the ESP image nor the SD copy is valid (an interrupted SD refresh leaves a manifest the stub refuses). | Run vios_sd_install.py install on the Mac, or reinstall the firmware. |
VIOSBOOT: 3 boots did not finish; staying in MOS (viosboot force) |
Three boots in a row loaded the resident but never reached a healthy state. | Type viosboot force at the MOS prompt to try again. Then look for the cause in the boot messages (a bad module or USER.FTH, see below). |
Boot hangs, or restarts by itself
Symptom: VIOS starts loading but never reaches vios>, or the ESP restarts on its own (the dev-mode watchdog).
Cause: the boot health signal never arrived. The resident reports HEALTH flag 0 when it starts and HEALTH flag 1 when its modules are loaded and checked. The ESP gives it 30 seconds. On an untethered board the ESP cannot reset the eZ80. It reports ERR CPU boot deadline in user mode; reset the board, or rolls back a firmware that is on trial. With the ZDI wire, the ESP restarts and reloads the eZ80 after each of the first two failures (BOOT failure 1/3; restarting ESP recovery). After the third it leaves the CPU halted with USB debug available (ERR three failed CPU boots; halted, USB recovery available).
Fix:
- Read the Console window or the events for
boot candidate rejected:,boot slot rejected:oruser startup failed:. These name the failing step. - A broken trial bundle falls back by itself on the next boot. A broken confirmed bundle falls back to the previous one. If both fail you get the built-in kernel (
boot recovery: built-in kernel, no valid source bundle) and can repair from there. - A runaway
USER.FTHorAUTORUN: from the Mac, remove or fix the file (:getit, edit it,:putit back). A runawayAUTORUNruns as a task, soTASKSandn TASK-KILLat the prompt stop it. - With the wire, after three failures the CPU stays halted. Use
debug_registersandmemory_readto look, thenrebootwithforce=true(CLI:recover). That command clears the failure count.
The new firmware rolled back
Symptom: vios_firmware_ab.py install ends with FIRMWARE rollback: <reason> and exits non-zero, and the old firmware is running again.
Cause: the new firmware was on trial and did not prove itself. The reason tells you why: CPU boot deadline, no CPU health, trial restarted 3 times by crashes, or the stub refused this image's resident and ran the SD copy.
Fix: nothing is lost. The previous slot is running and both backups are in your --backup-dir.
CPU boot deadlineorno CPU health: the resident inside the new firmware did not come up. Check that you built the pair in order and with matchingVIOS_INTERNAL_BAUDandVIOS_GUI(see Building), and thatmake testpasses.the stub refused this image's resident ...: the embedded resident is damaged or does not match the firmware's manifest. Rebuild both halves and install again.trial restarted 3 times by crashes: the firmware itself crashes. Try the probe environment or a previous build.
Check the result with vios_firmware_ab.py --port ... status. ERR FIRMWARE no other valid slot; trial ended, use esptool means there was nothing to fall back to. In that case install a known-good image, such as one from your backup directory, with vios_firmware_ab.py install.
The SD card is not mounted
Symptom: boot recovery: <error> at startup, DIR and OPEN fail, or sd_list returns an error.
Cause: no card, a card that is not FAT32 on an MBR partition table, or a card error.
Fix: reformat the card as FAT32 (MBR) on the Mac, preferably 32 GB or smaller, then run vios_sd_install.py install again. Copy anything you need off the card first. VIOS uses 8.3 file names, so long names created on the Mac show up in their short form. VIOS itself keeps running without a card, but nothing can be saved.
"VIOS wire integrity lost", and USB link statistics
Symptom: a tool fails with VIOS wire integrity lost; result cannot be trusted, or a result carries wire_loss_recovered: true.
Cause: the CH340 USB link occasionally drops a few bytes, about one frame in 1,500 to 4,000, whatever the cable or dock. The ESP numbers every frame and keeps the last 24, and the harness asks for any frame it misses. Normally you never notice. Integrity is lost only when a missing frame has already left the ring, or after ten requests without progress.
Fix:
- Check the counters with the
esp_statstool:link_requests(frames the host asked for),link_resent(frames resent) andlink_gone(frames that were no longer available). A few requests per hour is normal.link_goneabove 0 means the link got much worse: try another cable or port, or avoid a hub. - After a loss, run
:resync(or theresynctool) at an idle prompt. It never replays your last request, so check whether that request ran (for example with:snapshot) before repeating it. - If resync fails, restart the harness. This resets the board.
Display glitches
Symptom: half-drawn windows, stale text, or a corrupt workbench screen.
Fix:
- Ctrl+Alt+F1 twice switches to the text console and back. The display server repaints everything on return.
- In the workbench, use
WB-REPAINT, orWB-REDRAWfor a full reset (wb_redrawfrom the Mac). - From the Mac,
esp_guishows the display server's status line including its protocol error count.esp_statsshows frame timing (frame_work_max,vblank_start_max). - After an ESP-only restart, GUI applications built with the
UI-*words lose their windows (the workbench repaints itself). Restart the application.
The keyboard does nothing
Cause: the socket is PS/2, not USB. A USB-only keyboard gets power but sends nothing the ESP understands.
Fix: use a PS/2 keyboard, or a keyboard that supports PS/2 signalling through the USB-A-shaped socket. Check that typing works at the vios> prompt. If keys reach the GUI but a window ignores them, the window manager may be using them: see The GUI. You can test from the Mac with esp_inject, which types at the PS/2 boundary. If injected text works and the keyboard does not, the problem is the keyboard. The mouse is not supported.
A stuck task, or a program that will not stop
Symptom: output keeps coming, or the system feels slow.
Fix:
TASKS
2 TASK-KILL
TASKS lists the four task slots with state, word, steps and errors. n TASK-KILL stops task n and closes its handles. A foreground line that loops forever blocks the REPL. From the Mac, :recover (or reboot with force=true) restarts the system; with the ZDI wire this always works, even if the VDP is wedged. On an untethered board, press reset or power-cycle the board.
Finding out what went wrong: LAST-ERROR
When a word fails, VIOS records the failure before it unwinds the stacks: the message, the failing word and offset, the source token, the callers, and the data stack as it was. For example:
: INNER ( n -- ) 0 / ;
: OUTER 10 20 INNER ;
OUTER
LAST-ERROR
LAST-ERROR prints a compact machine-readable record: names and the message are hex-encoded so that the line survives any terminal. From the Mac, :why decodes it:
error #1: arithmetic error in INNER+4 at `/` (DIRECT, base 10)
called from OUTER+11 at `INNER`
data stack (top first, 3 deep): 0 20 10
The offsets depend on the engine. [dictionary changed since] warns that you redefined words after the error, so names may no longer match. TRACE word n (CLI :trace word n) single-steps a word and shows each step with the stack. See The Console and REPL.
Recovering a board that will not come up
Work down this list:
- Reset or power-cycle. Most problems are RAM state. Saved modules come back.
- Let the boot layers fall back. A failed trial bundle falls back to the confirmed one, a failed confirmed bundle to the previous one, and a failed ESP image to the SD copy. If the stub gave up, type
viosboot forceorviosboot sdat the MOS prompt. - Revert modules or bundles from a working prompt:
MODULE-ROLLBACK,BOOT-REVERT(see Modules and Persistence). - Repair the card on the Mac with
vios_sd_install.py install /Volumes/AGON. - Go back to the previous firmware with
vios_firmware_ab.py --port ... rollback, or install a slot image from your backup directory. - Fit the ZDI wires (see Hardware and Wiring). The ESP then loads VIOS straight into RAM over ZDI, independent of the card and of MOS, and the Mac can inspect and reset the CPU.
The eZ80 flash (MOS) is not part of this list. It is never written by an install or a recovery. Rewriting it (tools/vios_ez80_flash.py, wire required, --approved) is a last resort for a damaged MOS, not a fix for VIOS problems.