Description
When running ThinkRail on a remote / headless Linux host (e.g., accessed via SSH port forwarding), clicking the "Open project" button in the Web UI does nothing and fails completely silently.
Root Cause
In packages/server/src/dialog/dialog.ts:
pickersFor("linux") lists zenity and kdialog with nonZeroExit: "cancel".
- On a headless Linux server (or SSH session without
$DISPLAY / X11), zenity --file-selection --directory exits with code 1 (Gtk-WARNING **: Failed to open display).
selectDirectory() catches the non-zero exit code and interprets it as user cancellation because picker.nonZeroExit === "cancel", returning { path: null }.
- As a result, the failure to open the display is masked as a user cancellation, causing the "Open project" button in the web UI to fail silently without any error notification or fallback input.
Steps to reproduce
- Run
thinkrail on a headless Linux host (e.g., via SSH with DISPLAY unset).
- Connect to the web UI from a browser (e.g., via SSH port forwarding to
http://localhost:24242).
- Click "Open project" in the web UI.
Expected behavior
- If desktop pickers fail (or in a remote/headless setup), provide a fallback in-browser path input modal.
- At minimum, distinguish actual user cancellation from picker execution failure (e.g., missing
$DISPLAY) and report an error to the frontend so the user knows what happened.
Actual behavior
zenity crashes with Failed to open display, which is swallowed as null, causing the button click to do nothing.
Environment
- OS: Ubuntu 24.04 (Linux x86_64)
- ThinkRail version: 0.0.19
- Node version: v26.6.0
Description
When running ThinkRail on a remote / headless Linux host (e.g., accessed via SSH port forwarding), clicking the "Open project" button in the Web UI does nothing and fails completely silently.
Root Cause
In
packages/server/src/dialog/dialog.ts:pickersFor("linux")listszenityandkdialogwithnonZeroExit: "cancel".$DISPLAY/ X11),zenity --file-selection --directoryexits with code 1 (Gtk-WARNING **: Failed to open display).selectDirectory()catches the non-zero exit code and interprets it as user cancellation becausepicker.nonZeroExit === "cancel", returning{ path: null }.Steps to reproduce
thinkrailon a headless Linux host (e.g., via SSH withDISPLAYunset).http://localhost:24242).Expected behavior
$DISPLAY) and report an error to the frontend so the user knows what happened.Actual behavior
zenitycrashes withFailed to open display, which is swallowed asnull, causing the button click to do nothing.Environment