Skip to content

dev: renderer crash "Cannot read properties of null (reading 'useRef')" — stale HTTP cache resurrects previous Vite dep-optimizer generation (duplicate React) #4775

Description

@me2seeks

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:

  1. 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.
  2. 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.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions