Skip to content

fix(viz): keep the server up when the browser opener is missing - #820

Open
shu-chen-dot-ca wants to merge 1 commit into
trailhq:mainfrom
shu-chen-dot-ca:fix-819-viz-missing-opener
Open

shu-chen-dot-ca wants to merge 1 commit into
trailhq:mainfrom
shu-chen-dot-ca:fix-819-viz-missing-opener

Conversation

@shu-chen-dot-ca

Copy link
Copy Markdown

Summary

Fixes #819

On Linux without xdg-open (WSL, minimal images), viz printed its URL and then exited with status 1 on Error: spawn xdg-open ENOENT. spawn reports a missing executable later, as an 'error' event on the child. Nothing listened for it, so Node threw it and the server went down with the process. openBrowser had the same gap: its try/catch only sees errors thrown while spawn is called, not this later event.

Changes

  • src/util/browser.ts: openBrowser adds a no-op 'error' listener to the child before unref(). As before, the printed URL is the fallback.
  • src/cli.ts: viz calls openBrowser(srv.url) instead of its own copy of the opener logic, and the unused spawn import goes. Because of that, viz now also respects GRAFT_NO_BROWSER, like the sign-in flow already does. --no-open is unchanged.
  • test/browser-opener.test.ts (new, Linux only, since macOS always has open and Windows goes through the shell). Both tests run with a PATH that has no xdg-open:
    • one calls openBrowser in a child process and checks the process is still alive 500 ms later;
    • one runs viz from src/cli.ts and checks the server is still running a second after it prints the URL, and that /api/context-graph answers 200.

Testing

On Ubuntu with Node 24.22:

  • Reproduced the crash with the built CLI and a PATH without xdg-open: viz exited with status 1, spawn xdg-open ENOENT. With this branch the server stays up and serves the page (HTTP 200). Screenshots below.
  • test/browser-opener.test.ts: 2/2 fail with main's src/ and 2/2 pass here.
  • Other opener cases, run by hand against the built CLI:
    • a working xdg-open stand-in still gets the URL;
    • an opener that exits 1 leaves the server running (200);
    • an opener that isn't executable (EACCES) leaves it running too (200);
    • --no-open still serves (200);
    • GRAFT_NO_BROWSER=1 no longer calls the opener.
  • tsc -p tsconfig.json --noEmit is clean.
  • Full npm test (TZ=UTC GRAFT_TEST_CONCURRENCY=4, with ~/.trail and ~/.graft cleared first): 4493 pass, 0 fail, 8 skipped.
    • TZ=UTC matches CI. In a US Eastern timezone, test/share-session.test.ts expects Oct 1 and gets Sep 30, on main too.
    • A second full run in the same home folder failed 3 tests in trail-scenario-hooks.test.ts and 1 in lesson-index-1b.test.ts. They failed on main as well, because of ~/.trail/sessions/*.ship-checks.json and ~/.trail/repos that the first run had written. Clearing those fixed it.

Screenshots

Before After
before after

@trailhq-graft

trailhq-graft Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

🌱 graft blast radius

Nothing outside this diff depends on it. 2 areas changed; no indexed dependents at depth 2.
Tests: no test reaches Browser Launch.
Tag: @anirudhkumar-nanonets — CLI, Browser Launch · @shhdwi — CLI

Who knows this code — 2 people across 2 areas
Area Who knows it
CLI · changed @anirudhkumar-nanonets — 71 commits, last 4d ago · @shhdwi — 51 commits, last 15d ago
Browser Launch · changed @anirudhkumar-nanonets — 1 commit, last 29d ago

Ownership is git history over each area's own files, weighted towards recent work (120-day half-life). Merge commits and bots are dropped, and you are dropped from your own PR. A name with no @ has no GitHub handle in its commit email — tag them by hand, or add a .mailmap entry. A suggestion from history, not a CODEOWNERS rule.

Test signal per changed area — 1 ✗ · 1 –

Reached = a node under a test path has a resolved edge into the changed symbol. It undercounts anything called indirectly — through a CLI, a spawned process or a dynamic import — so read a low ratio as “look here”, never as a coverage gate.

  • – CLI — no function, method or class changed here
  • ✗ Browser Launch — 0 of 1 reached · no test file reaches it
    • not reached: openBrowser

graft blast · origin/main...HEAD · depth 2 · 3 changed files

Open the interactive graph → — click an area to see its dependent symbols at file:line.

github-actions Bot added a commit that referenced this pull request Nov 12, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(viz): missing xdg-open crashes the visualization server on WSL/Linux

1 participant