Troubleshooting

Common issues when running EMOS recipes, organized by error message.


“Sensor topic not found within 10s”

The most common error. emos run checks that every sensor topic declared in the recipe is actually publishing before launching.

Possible causes:

Cause

Fix

Driver not installed

Run emos info <recipe> for suggested packages, then sudo apt install <package>

Driver installed but not running

Start the driver node in a separate terminal before running the recipe

Topic name mismatch

Compare ros2 topic list output with Topic(name=...) in your recipe and correct the name

Container mode: driver not installed in container

Install the driver inside the container: docker exec -it emos bash -c "apt-get update && apt-get install -y ros-jazzy-usb-cam" and launch it

Diagnosis:

# Check what topics exist
ros2 topic list

# Check if a specific topic has data
ros2 topic hz /image_raw

If the topic exists but shows 0 Hz, the driver is running but not producing data (check hardware connections).

To temporarily bypass this check while debugging, use emos run <recipe> --skip-sensor-check.


“ImportError: No module named ‘agents’”

EMOS Python packages are not on the Python path. The fix depends on your install mode:

Mode

Fix

Container

Run the recipe via emos run, not directly with python3. The CLI executes inside the container where packages are installed.

Native

Source the ROS2 environment first: source /opt/ros/jazzy/setup.bash

Pixi

Enter the pixi shell first: pixi shell, then source install/setup.sh


“Robot plugin not found”

A recipe imports a robot plugin and hands it to the launcher – from myrobot_plugin import MyRobotPlugin / Launcher(robot_plugin=MyRobotPlugin()) – but the plugin isn’t installed, so the recipe fails at startup with something like ModuleNotFoundError: No module named 'myrobot_plugin'.

Fix: install the plugin with the CLI – it clones, builds it for your install mode, and wires it so recipes can import it:

emos plugin list                       # find the plugin's name
emos plugin install <plugin>           # install + activate it
emos plugin inspect                    # confirm it's the active plugin

A robot runs one plugin at a time, so installing a new one replaces the active one. If the import still fails right after installing, make sure you’re launching through emos run (or, in development, that you’ve sourced the plugin overlay – see Running Recipes).

If the plugin is one you wrote (not in the catalog), build it into the EMOS workspace and follow the authoring guide.

See also


“Zenoh router failed to start”

The Zenoh router (used by rmw_zenoh_cpp) typically fails when port 7447 is already in use from a previous run.

Fix:

# Kill any existing Zenoh routers
pkill -f rmw_zenohd

# Then retry
emos run <recipe>

To use a different RMW and skip Zenoh entirely:

emos run <recipe> --rmw rmw_cyclonedds_cpp

“Recipe hangs with no output”

The recipe started but nothing happens. Two common causes:

1. Sensor topic exists but has no data (0 Hz)

The driver node is running but the hardware isn’t producing data. Check:

ros2 topic hz /image_raw    # Should show non-zero rate

If 0 Hz: check physical connections (USB cable, power), device permissions (sudo chmod 666 /dev/video0), or driver configuration.

2. Model inference failing

EMOS verifies that the model server is reachable when the recipe starts. If the recipe hangs after that, the server is running but inference itself is failing — for example, the model isn’t loaded, is out of memory, or the request format is wrong.

Check your model server’s logs for errors. Common causes:

  • Model not pulled/loaded — e.g. for Ollama, run ollama pull qwen2.5vl:latest before starting the recipe

  • Out of memory — the model is too large for available RAM/VRAM. Try a smaller checkpoint.

  • Timeout on cloud endpoint — network latency or rate limiting. Check the provider’s status page.


“Container ‘emos’ not found”

The Docker container was removed or never created.

Fix:

emos install --mode container

This recreates the container. Your recipes in ~/emos/recipes/ are preserved (they’re mounted from the host).


“No EMOS installation found”

The config file at ~/.config/emos/config.json is missing.

Fix: Run emos install to set up EMOS — use emos install --mode pixi for the pixi mode.


Dashboard

The dashboard daemon (emos serve) and its web UI have a few common failure modes worth knowing about.

“address already in use” when starting emos serve

Another process is bound to the dashboard port (default 8765). Most often this is a previous emos serve that’s still running, either in another terminal or as a systemd service.

# Is it already running as a service?
systemctl status emos-dashboard.service

# Stop the service (or the foreground process), then retry
sudo systemctl stop emos-dashboard.service
emos serve

# Or pick a different port for one run
emos serve --addr :9000

# Or change the persisted port
emos config set port 9000

I lost the pairing code

The pairing code is only printed on first boot and never persisted in plaintext on disk. Rotate to issue a fresh one:

emos config rotate-pairing
# ✓ New pairing code (shown once): 829471

Already-paired browsers keep working — rotation is a code rotation, not a token revocation. Use emos config tokens to see who is paired and emos config revoke-token <id|label> to remove a specific browser.

emos.local does not resolve

mDNS (*.local) works on most laptops out of the box but is unreliable on phones (especially Android). The dashboard is also published as <device-name>.local — and the boot banner always prints LAN IPs you can use directly.

Fixes, in order of preference:

# 1. Use the device-specific name printed in the banner
ping epic-otter.local

# 2. Use a LAN IP from the banner
http://192.168.1.42:8765/

# 3. Confirm avahi/Bonjour is running on the laptop
#    (Linux: avahi-daemon, macOS: built-in, Windows: Bonjour from the iTunes installer)

# 4. Force a fresh mDNS publication after a hostname change
emos config set name new-name
sudo systemctl restart emos-dashboard.service

For headless or factory networks, just use the IP address; the dashboard works identically over IP and mDNS.

Browser shows a persistent “Not Secure” warning under --tls

emos serve --tls mints a self-signed cert. Browsers always flag self-signed certs as Not Secure even after you click through the warning — the encryption is real, but the trust chain is not. To get rid of the lozenge:

  • Firefox: Settings → Privacy & Security → Certificates → View Certificates → Authorities → Import ~/.config/emos/tls.crt, tick “Trust this CA to identify websites.” Firefox keeps its own trust store separate from the OS.

  • Chrome / system-wide: install the cert into the OS trust store (update-ca-certificates on Linux, Keychain on macOS, certmgr on Windows).

Verify the cert before trusting it:

emos config tls-fingerprint

Compare the SHA-256 fingerprint with the one the browser shows under “View Certificate”.

See also

CLI → HTTPS (Optional) for the full rationale and trust-store steps.

After moving to a new network, HTTPS shows a hostname-mismatch error

The TLS cert’s SubjectAltNames are baked in at mint time. If the device’s IP or .local name changed, the cert no longer covers the address you’re using. Regenerate:

emos config tls-regenerate
sudo systemctl restart emos-dashboard.service

“TLS handshake error from …” floods the log

Pre-v0.6.2 — these were stdlib log lines bypassing slog. The current daemon routes them to slog at DEBUG (invisible at the default INFO level). If you still see them: probably a probe from a Tailscale agent, nmap, or a browser hitting the port over plain HTTP while the daemon is in --tls mode. Run emos serve -v to see the chatter when debugging.

“503 service_unavailable, code: offline” on the recipe catalog

The dashboard tried to reach the Automatika recipe server and the device’s reachability probe failed. This is by design — already-installed recipes still run while offline. If you expected the device to be online:

# Force a fresh probe (bypasses the 30 s cache)
curl http://emos.local:8765/api/v1/connectivity?refresh=1

# DNS, default route, firewall — the usual suspects
ping support.automatikarobotics.com

Dashboard shows “Not installed” but the CLI works

The dashboard daemon reads ~/.config/emos/config.json once at startup and serves that snapshot for its whole lifetime. So if emos serve was started before EMOS finished installing, the daemon keeps reporting “not installed”, even though the CLI (which re-reads the config on every command) already sees the install. A browser refresh won’t help: /info reflects the daemon’s cached config, not the current file on disk.

Fix: make sure the config exists, then restart the daemon so it re-reads it.

emos config show     # materialises / migrates ~/.config/emos/config.json if needed

# then restart the dashboard so it picks up the config:
sudo systemctl restart emos-dashboard.service   # if running as a service
# or, if you started it manually, stop `emos serve` (Ctrl-C) and run it again

Refresh the browser afterwards.