Description
When Hermes Dashboard is registered as a Windows Scheduled Task (LogonTrigger + InteractiveToken) and runs automatically at user logon, it exits with code 1 if the Vite web UI build fails — leaving port 9119 unreachable. The same build succeeds when run manually from an interactive terminal.
This is not a PATH or S4U issue (the task uses correct InteractiveToken with full user environment). The problem is architectural: the Dashboard has no fallback or retry when its internal npm run build fails.
Steps to Reproduce
- Install Hermes on Windows 11
- Register Dashboard as a Scheduled Task (correctly configured: LogonTrigger + InteractiveToken, via VBS wrapper)
- Reboot, or run
hermes update (which replaces web source files)
- Access http://127.0.0.1:9119 → connection refused
- Check
~/.hermes/dashboard-startup.log → shows ✗ Web UI build failed
Expected Behavior
The Dashboard should handle web UI build failures gracefully:
- If a stale
web_dist/ exists, serve it as a fallback (better than nothing)
- Or retry the build once before giving up
- Or accept a
--skip-build / --prebuilt flag so the caller can guarantee the dist is ready
Actual Behavior
Dashboard exits with code 1. HTTP server is never started. The only way to recover is to manually cd web && npm run build in a terminal, then restart.
Root Cause
In hermes_cli/main.py, the startup flow is:
start()
→ _web_ui_build_needed() # checks if dist is missing or stale
→ _build_web_ui() # runs npm run build via subprocess
→ start HTTP server # never reached if build fails
Three specific gaps:
-
No --skip-build / --prebuilt flag — the caller cannot tell the Dashboard "the dist is already built, don't try to build." A Windows Scheduled Task could then pre-build in the BAT wrapper and start the Dashboard with --skip-build, avoiding the internal build entirely.
-
No fallback to stale dist — if web_dist/index.html exists but is stale (source files modified), the Dashboard could serve the stale version and let the user refresh after a manual build, rather than exiting with code 1. Many CI/CD systems (including npm run build output resolution across machines) produce identical build artifacts; a stale UI is far better than no UI.
-
No retry — _build_web_ui() calls subprocess.run() once. If it fails (e.g. npm not fully initialized at boot, antivirus scanning Node.js binaries, transient disk I/O), the process exits immediately. A simple 1-retry with a 3-second delay would cover most boot-time race conditions.
Additionally, the npm build failure details (stderr) are not surfaced in the dashboard startup log — the error message just says ✗ Web UI build failed with no information about why. Adding the captured stderr to the log output would significantly improve debugging.
Workaround (for users hitting this)
Add a pre-build step in the BAT startup script before calling hermes dashboard:
set WEB_DIR=%LOCALAPPDATA%\hermes\hermes-agent\web
cd /d "%WEB_DIR%"
npm run build >nul 2>&1
if %ERRORLEVEL% neq 0 (
timeout /t 3 /nobreak >nul
npm run build >nul 2>&1
)
cd /d "%~dp0"
This builds the web UI in the BAT's environment (which has full PATH) before the Dashboard tries to do it internally. Since _web_ui_build_needed() checks source file mtimes vs dist, the internal build is skipped if the dist is already fresh.
Environment
- Windows 11 (version 10.0.26200.8328)
- Hermes latest (git checkout)
- Node.js v24.15.0, npm 11.12.1
- Scheduled Task: LogonTrigger + InteractiveToken (correct configuration)
Description
When Hermes Dashboard is registered as a Windows Scheduled Task (LogonTrigger + InteractiveToken) and runs automatically at user logon, it exits with code 1 if the Vite web UI build fails — leaving port 9119 unreachable. The same build succeeds when run manually from an interactive terminal.
This is not a PATH or S4U issue (the task uses correct InteractiveToken with full user environment). The problem is architectural: the Dashboard has no fallback or retry when its internal
npm run buildfails.Steps to Reproduce
hermes update(which replaces web source files)~/.hermes/dashboard-startup.log→ shows✗ Web UI build failedExpected Behavior
The Dashboard should handle web UI build failures gracefully:
web_dist/exists, serve it as a fallback (better than nothing)--skip-build/--prebuiltflag so the caller can guarantee the dist is readyActual Behavior
Dashboard exits with code 1. HTTP server is never started. The only way to recover is to manually
cd web && npm run buildin a terminal, then restart.Root Cause
In
hermes_cli/main.py, the startup flow is:Three specific gaps:
No
--skip-build/--prebuiltflag — the caller cannot tell the Dashboard "the dist is already built, don't try to build." A Windows Scheduled Task could then pre-build in the BAT wrapper and start the Dashboard with--skip-build, avoiding the internal build entirely.No fallback to stale dist — if
web_dist/index.htmlexists but is stale (source files modified), the Dashboard could serve the stale version and let the user refresh after a manual build, rather than exiting with code 1. Many CI/CD systems (includingnpm run buildoutput resolution across machines) produce identical build artifacts; a stale UI is far better than no UI.No retry —
_build_web_ui()callssubprocess.run()once. If it fails (e.g. npm not fully initialized at boot, antivirus scanning Node.js binaries, transient disk I/O), the process exits immediately. A simple 1-retry with a 3-second delay would cover most boot-time race conditions.Additionally, the npm build failure details (stderr) are not surfaced in the dashboard startup log — the error message just says
✗ Web UI build failedwith no information about why. Adding the captured stderr to the log output would significantly improve debugging.Workaround (for users hitting this)
Add a pre-build step in the BAT startup script before calling
hermes dashboard:This builds the web UI in the BAT's environment (which has full PATH) before the Dashboard tries to do it internally. Since
_web_ui_build_needed()checks source file mtimes vs dist, the internal build is skipped if the dist is already fresh.Environment