What happened
In a dev session (npm run dev), opening Settings crashed the renderer:
TypeError: Cannot read properties of null (reading 'useRef')
at exports.useRef (.../apps/desktop/node_modules/.vite/deps/react.js?v=3911c03d:745:30)
at SettingsModal (/settings/settings-modal.tsx:31:18)
The error boundary caught it and showed the crash surface; reloading the window "fixed" it. The crash recurs across dev restarts whenever the conditions below line up.
Root cause: two React module instances in one page
resolveDispatcher() returned null for the react copy that settings-modal.tsx imported, while react-dom was running on a different react copy. The diagnostic stack shows the page executed two Vite dep-optimizer generations at once:
react.js?v=3911c03d — the generation freshly committed by the running dev server
client-CwWlPFAR.js, ToastViewport-*.js, Layer-*.js, i18n-*.js, theme-*.js all with ?v=d164f9b2 — a previous optimizer generation, no longer present on disk
Evidence from the Electron HTTP cache (~/.config/Maka Dev/Cache): it holds many historical generations, e.g. a cached react-dom_client.js?v=c163ffce whose body imports client-CwWlPFAR.js?v=d164f9b2, which in turn imports react.js?v=4a8fd6a8. Because Vite serves deps as Cache-Control: max-age=31536000,immutable and preserves per-dep browserHash labels across re-optimization commits, a stale cached chunk graph can be resurrected intact days later, next to freshly transformed source files that import the current react.js. Two React instances → the dispatcher is only set on one → the first hook call in any lazily loaded component throws and takes down the renderer.
Two repo-side gaps make this reachable:
- The main process never clears the renderer HTTP cache in dev. Old immutable dep chunks (
?v=<old generation>) stay loadable forever across dev-server restarts.
scripts/dev.mjs launches Electron immediately after server.listen(), while Vite's initial dependency crawl / optimizer commit is still in flight (observed: deps committed at T+2.4s, app ready at T+3.2s). Modules loaded during that window can pin URLs from the pre-commit state.
Also observed: two abandoned node_modules/.vite/deps_temp_* directories from an earlier session, i.e. mid-session re-optimizations (late-discovered deps such as mermaid diagram chunks) do happen in this workspace — each one rotates browserHash and relies on the full-reload reaching the page.
Environment
Maka 0.2.0, dev channel, Electron 43.4.1, Vite 8.2.2, React 19.2.8, Ubuntu (linux 7.0.0-30-generic x64). Dev-only issue: packaged builds load file:// bundles without the dev HTTP cache.
Proposed fix
- In
main-window.ts, clear the session HTTP cache before loadURL when rendererEntry.useDevServer (dev-only; packaged builds unaffected).
- In
scripts/dev.mjs, after server.listen(), warm the renderer entry and wait for the crawl (server.environments.client.warmupRequest('/main.tsx') + waitForRequestsIdle()) before launching Electron, so the initial optimizer commit lands before the window loads.
I have a patch ready and will open a PR shortly.
What happened
In a dev session (
npm run dev), opening Settings crashed the renderer:The error boundary caught it and showed the crash surface; reloading the window "fixed" it. The crash recurs across dev restarts whenever the conditions below line up.
Root cause: two React module instances in one page
resolveDispatcher()returnednullfor thereactcopy thatsettings-modal.tsximported, whilereact-domwas running on a differentreactcopy. The diagnostic stack shows the page executed two Vite dep-optimizer generations at once:react.js?v=3911c03d— the generation freshly committed by the running dev serverclient-CwWlPFAR.js,ToastViewport-*.js,Layer-*.js,i18n-*.js,theme-*.jsall with?v=d164f9b2— a previous optimizer generation, no longer present on diskEvidence from the Electron HTTP cache (
~/.config/Maka Dev/Cache): it holds many historical generations, e.g. a cachedreact-dom_client.js?v=c163ffcewhose body importsclient-CwWlPFAR.js?v=d164f9b2, which in turn importsreact.js?v=4a8fd6a8. Because Vite serves deps asCache-Control: max-age=31536000,immutableand preserves per-depbrowserHashlabels across re-optimization commits, a stale cached chunk graph can be resurrected intact days later, next to freshly transformed source files that import the currentreact.js. Two React instances → the dispatcher is only set on one → the first hook call in any lazily loaded component throws and takes down the renderer.Two repo-side gaps make this reachable:
?v=<old generation>) stay loadable forever across dev-server restarts.scripts/dev.mjslaunches Electron immediately afterserver.listen(), while Vite's initial dependency crawl / optimizer commit is still in flight (observed: deps committed at T+2.4s, app ready at T+3.2s). Modules loaded during that window can pin URLs from the pre-commit state.Also observed: two abandoned
node_modules/.vite/deps_temp_*directories from an earlier session, i.e. mid-session re-optimizations (late-discovered deps such as mermaid diagram chunks) do happen in this workspace — each one rotatesbrowserHashand relies on the full-reload reaching the page.Environment
Maka 0.2.0, dev channel, Electron 43.4.1, Vite 8.2.2, React 19.2.8, Ubuntu (linux 7.0.0-30-generic x64). Dev-only issue: packaged builds load
file://bundles without the dev HTTP cache.Proposed fix
main-window.ts, clear the session HTTP cache beforeloadURLwhenrendererEntry.useDevServer(dev-only; packaged builds unaffected).scripts/dev.mjs, afterserver.listen(), warm the renderer entry and wait for the crawl (server.environments.client.warmupRequest('/main.tsx')+waitForRequestsIdle()) before launching Electron, so the initial optimizer commit lands before the window loads.I have a patch ready and will open a PR shortly.