Diagnostics
What tools Roves gives you for figuring out what went wrong, in general.
A double-clicked Roves build has no console — Windows builds set #![windows_subsystem = "windows"] specifically so it never flashes one — so failures don't show up the way a
terminal app's would. Whatever you're actually debugging (a silent launch, a crash mid-game,
a rendering glitch, a Steam integration that isn't responding), the same handful of tools
below are where to start.
Check roves.log first
Every launch writes a fresh log (truncated on each run, so you're never looking at stale output from a previous launch) to the same per-game directory content extraction uses:
- Windows:
%LOCALAPPDATA%\<game name>\roves.log - macOS:
~/Library/Caches/<game name>/roves.log - Linux:
~/.cache/<game name>/roves.log
It captures the engine's own logging, your game's console.log/warn/error output, and
any panic — all in one place, from the very first line of startup. RUST_LOG=debug (or
trace) before launching raises the verbosity if the default level doesn't show enough.
Only one line in the log, then nothing?
That's consistent with either a hang, or a native crash (a GPU/driver issue, or a missing system DLL) that happens before the point your build actually reached — not a Rust panic, which would always add at least one more line. Check which milestone was logged last to narrow down where.
A black window isn't always silent anymore
If the page's own top-level <script> fails to load, the engine now shows a visible error
screen (the message plus a pointer to roves.log) instead of a plain black window — see the
root-relative-path incident below for the most common way to hit this. A black window with
no error screen and no new log line at all past startup is still the hang/crash case
above, not this one.
A real incident: root-relative asset paths resolving to the OS filesystem root, not your game
A build reported a plain black window, no content ever rendering — roves.log showed:
ERROR script::script_module] Fetching module script failed Opening file failed
ERROR script::dom::html::htmlscriptelement] Fetching classic script failed Opening file failed (file:///C:/registerSW.js)The game's own build output referenced its scripts root-relative (/assets/index-XXXX.js) —
Vite's default, and every other major bundler's default too, since that content is normally
served over http(s) from an actual domain root. Roves opens index.html via a bare file:
URL instead (never an http(s) server): per the URL spec, a root-relative reference resolved
against a file: document becomes the real OS filesystem root (C:\assets\... on Windows,
/assets/... on Linux/macOS), not the game's own content directory — exactly what any
browser does for a file:// document opened directly instead of served. Every script the
page needed then failed to fetch, and nothing ever rendered.
This is now fixed at the engine level: a root-absolute reference that doesn't resolve for
real is retried rebased onto the game's own content root before failing — covers packed
content, --content-compress none, and a raw dev --url launch alike. If you're on an
older engine build than this fix and hit the exact log lines above, the workaround is
configuring your bundler for relative output instead (Vite: base: './').
A real incident: mach bundle's own flags leaking into the game's args
Early on, a build reported "nothing happens" with roves.log showing the resolved launch
args including --package-name/--package-version — flags meant for mach bundle itself
(naming a --msi/--deb/--dmg output), not for the running game. Roves' own CLI parser
rejects unrecognized flags outright and exits immediately, before a window ever exists —
exactly matching "nothing happens."
A first fix attempted to stop this at the source: mach bundle cross-checking every
passthrough token against its own registered flags and dropping any match before writing
launch.json. Real testing (downloading and running an actual CI-built bundle) showed this
didn't actually work — the exact mechanism still isn't pinned down; a byte-faithful
reconstruction of mach's own argument-parsing logic, fed the exact failing invocation,
parses cleanly in isolation, so the leak only manifests somewhere in the full mach
environment.
Given that, the actual fix stopped trying to guarantee launch.json is clean and made a
broken one non-fatal instead: if the bundled launch args fail to parse, the engine now logs
the failure and retries once with just the content URL, dropping every extra arg, rather than
exiting before a window ever appears. Worst case you lose that launch.json's window-size/
title customization — the game still starts. A real CLI invocation with a typo'd flag is
unaffected and still errors normally; this retry only applies to a bundled (double-click)
launch. If you're building at an older revision than this fix, launch.json next to your
bundle's executable is still worth checking for anything that looks like it belongs to mach bundle rather than your game — it explains the shape of the failure even if your build
predates the retry logic that survives it.
A faster way to check: --diagnostic-script
mach bundle --diagnostic-script ships a diagnose.bat (Windows) / diagnose.sh
(macOS/Linux) right next to the game binary — including inside the --msi/--dmg installer
outputs, since they wrap the same staged files (not --deb, where a terminal already shows
output directly). Running it instead of the game launches the exact same binary from a
console that stays open afterward, printing the exit code and the newest roves.log's
contents inline. It exists specifically so a non-technical tester who hits "I double-clicked
it and nothing happened" can run one script and paste back something useful, instead of being
walked through finding %LOCALAPPDATA% by hand. Off by default — add the flag when building a
test/debug bundle you expect to hand to testers.
CI catches this now too
.github/workflows/test.yml used to only confirm mach bundle succeeds — which is exactly
how the incident above went unnoticed through several green CI runs: every real launch of the
resulting bundle crashed instantly while CI stayed green throughout, because nothing there had
ever actually run the binary. CI now launches each bundle it builds for a few seconds and
fails the job if the process doesn't stay up, on every platform (Linux runs under xvfb-run;
Windows and macOS have a real window server already). A build succeeding and a build
launching are different claims — if you're setting up your own CI around mach bundle (or
roves-action), consider adding an
equivalent check rather than treating a successful bundle step as proof the game runs.
roves-action now has exactly this: its own .github/workflows/test.yml builds a real game
(pixi-vn-react-template)
through the action on every push — it's what surfaced the root-relative-path incident above
in the first place, not a synthetic fixture that happened to avoid it.
Still stuck?
Open an issue on the repo with
roves.log's contents attached — the exact last line logged is the single most useful
piece of information for narrowing down where to look next.