Skip to content

fix(desktop): keep dev renderer on one Vite dep-optimizer generation - #4782

Open
me2seeks wants to merge 1 commit into
apache:mainfrom
me2seeks:fix/desktop-dev-optimizer-stale-cache
Open

fix(desktop): keep dev renderer on one Vite dep-optimizer generation#4782
me2seeks wants to merge 1 commit into
apache:mainfrom
me2seeks:fix/desktop-dev-optimizer-stale-cache

Conversation

@me2seeks

@me2seeks me2seeks commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Summary

In dev sessions, opening Settings (or any lazily loaded component that runs hooks) could crash the renderer with TypeError: Cannot read properties of null (reading 'useRef'). The page was executing two React module instances: freshly transformed sources import the current generation's react.js, while react-dom and several chunks were resurrected from the persistent Electron HTTP cache out of a previous Vite dep-optimizer generation. The dispatcher is only set on the instance react-dom renders with; hooks called through the other instance see null and throw.

Two changes, both dev-only (packaged builds load file:// and are unaffected):

  1. main-renderer-dev-cache.ts (new): clear the session HTTP cache before loading a Vite dev server, so immutable ?v=<browserHash> chunks from older generations can never be resurrected. Failure downgrades to a warning instead of blocking window creation. Wired into main-window.ts right before loadMainRenderer.
  2. dev.mjs: after server.listen(), warm the renderer entry (warmupRequest('/main.tsx')) and waitForRequestsIdle() before launching Electron, so the initial dependency crawl / optimizer commit lands before the window starts loading modules.

Fixes #4775

Verification

  • tsc -p tsconfig.main.json --noEmit
  • npm --workspace @maka/desktop run build:main
  • node --test dist/main/__tests__/main-renderer-dev-cache.test.js — 3 pass (clears in dev / untouched for packaged / failure downgrades) ✓
  • node --test scripts/dev-app-runtime.test.mjs
  • biome check on touched files ✓
  • Live API check: standalone Vite 8.2.2 server with the repo config (throwaway cacheDir): environments.client.warmupRequest('/main.tsx') + waitForRequestsIdle() settle in ~2.4s with the entry graph crawled and the dep optimizer bundling ✓
  • Not run: full npm test, e2e suites (heavier than this change; happy to run on request).

Root cause

Evidence gathered on the affected machine (full write-up in #4775):

  • The crash stack mixes two ?v= browser hashes: react.js?v=3911c03d (current generation) vs. client-CwWlPFAR.js, ToastViewport-*.js, Layer-*.js, i18n-*.js, theme-*.js all at ?v=d164f9b2 (a generation no longer on disk).
  • The Electron HTTP cache (~/.config/Maka Dev/Cache) held multiple historical generations, e.g. a cached react-dom_client.js?v=c163ffce whose body imports client-CwWlPFAR.js?v=d164f9b2, which imports react.js?v=4a8fd6a8 — an intact stale chunk graph, loadable forever thanks to Cache-Control: immutable.
  • node_modules/.vite/deps_temp_* leftovers show mid-session re-optimizations happen in this workspace (late-discovered deps), each rotating browserHash.
  • dev.mjs launched Electron immediately after server.listen(); observed timings had the deps commit land at T+2.4s and the app ready at T+3.2s — page load raced the generation swap.

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: Maka (AI agent) — reproduced the diagnosis from on-disk evidence (Vite optimizer metadata, Electron HTTP cache), authored the patch and its tests. Commit carries the Generated-by: Maka trailer.

Checklist

  • Tests cover the change and fail without it (dev-cache helper: clears in dev / leaves packaged builds alone / degrades to warning)
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

Opening Settings in a dev session could kill the renderer with
"Cannot read properties of null (reading 'useRef')": the page executed
two optimizer generations at once — react/react-dom chunks resurrected
from the persistent Electron HTTP cache (immutable `?v=<browserHash>`
entries whose per-dep labels survive re-optimization commits) next to
freshly transformed sources importing today's react copy. Two React
instances leave the hook dispatcher null on one of them, and the first
hook call in any lazily loaded component crashes.

- main-window: clear the session HTTP cache before loading a dev server
  (main-renderer-dev-cache.ts, packaged file:// builds untouched)
- dev.mjs: warm the renderer entry and wait for the crawl to settle
  before launching Electron, so the initial optimizer commit lands
  before the window loads

Fixes apache#4775

Generated-by: Maka
@github-actions github-actions Bot added the effort/M Under 500 readable lines label Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/M Under 500 readable lines

Projects

None yet

1 participant