usestrix/strix · error · SystemExit

Viewer UI is not built.\nBuild it with: cd…

Error message

Viewer UI is not built.\nBuild it with: cd strix/interface/viewer/frontend && npm ci && npm run build

What it means

SystemExit(1) with 'Viewer UI is not built' raised by the viewer CLI (strix/interface/viewer/cli.py main()) when bundle_is_built() reports the frontend asset bundle is missing. The viewer serves a pre-built JS/CSS bundle from strix/interface/viewer/frontend; when the package was installed from source without building the frontend, serving is impossible, so the CLI exits with the exact build command to run.

Solutions

  1. Run the exact command shown: cd strix/interface/viewer/frontend && npm ci && npm run build (requires Node.js/npm installed)
  2. If Node is missing, install it first (e.g. via nvm or system package manager), then re-run the build
  3. Use an official release wheel/PyPI install, which ships the pre-built bundle, instead of a source checkout
  4. In Dockerfiles, make the frontend build step fatal (set -e) so a broken build fails the image build instead of deferring to runtime

Example fix

# before (Dockerfile, silent failure)
RUN cd strix/interface/viewer/frontend && npm ci && npm run build || true
# after
RUN cd strix/interface/viewer/frontend && npm ci && npm run build
Defensive patterns

Strategy: validation

Validate before calling

from strix.interface.viewer.bundle import bundle_is_built  # adjust import to actual module

def assert_viewer_ready():
    if not bundle_is_built():
        raise SystemExit(
            "Viewer UI missing. Run: cd strix/interface/viewer/frontend && npm ci && npm run build"
        )

Try / catch

try:
    main(argv)
except SystemExit as e:
    if e.code == 1 and not bundle_is_built():
        subprocess.run(["npm", "ci"], cwd="strix/interface/viewer/frontend", check=True)
        subprocess.run(["npm", "run", "build"], cwd="strix/interface/viewer/frontend", check=True)
        main(argv)
    else:
        raise

Prevention

When it happens

Trigger: Running the viewer (e.g. `strix viewer` / python -m strix.interface.viewer.cli) from a source checkout or a wheel built without the frontend step. bundle_is_built() finds no dist bundle, prints the message, and raises SystemExit(1) before binding any port.

Common situations: Fresh git clone + `uv run strix ...` without building the frontend; CI installing the package with --no-build-isolation that skips the npm step; node/npm absent from the image so the optional frontend build silently skipped; downstream packagers (Debian, conda) shipping a Python-only build; npm ci failing midway in a Docker build and the error being ignored.

Related errors


AI-assisted analysis of usestrix/strix@8551339130 (2026-08-15). Data as JSON: /api/errors/551d957393e61818. Report an issue: GitHub.

Appendix: source

Thrown at strix/interface/viewer/cli.py:63

        default=0,
        help="Port to serve on (default: an available ephemeral port).",
    )
    parser.add_argument("--host", default="127.0.0.1", help=argparse.SUPPRESS)
    parser.add_argument(
        "--no-open",
        action="store_true",
        help="Do not open the browser automatically.",
    )
    args = parser.parse_args(argv)

    console = Console()

    if not bundle_is_built():
        console.print(
            "[bold red]Viewer UI is not built.[/]\n"
            "Build it with: [cyan]cd strix/interface/viewer/frontend && npm ci && npm run build[/]"
        )
        raise SystemExit(1)

    run_dir = _resolve_run_dir(args.run, console)

    httpd, url, token = serve(
        run_dir,
        host=args.host,
        port=args.port,
        open_browser=not args.no_open,
    )
    # The tokened URL is what authorizes the browser (steering, report sending,
    # history). Print it rather than the bare URL so the operator -- and only
    # the operator -- can open or share an authorized link.
    open_url = authorized_url(url, token)

    run_name = run_dir.name
    summary = read_run_summary(run_dir)
    live = not summary.get("finished", False)

View on GitHub (pinned to 8551339130)