{"record":{"id":"c32056ac44094f27","repo":"stablyai/orca","slug":"electron-did-not-expose-gc-keep-js-flags-expo","errorCode":null,"errorMessage":"Electron did not expose GC; keep --js-flags=--expose-gc in the harness","messagePattern":"Electron did not expose GC; keep --js-flags=--expose-gc in the harness","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"config/scripts/hang-watchdog-memory-benchmark.mjs","lineNumber":42,"sourceCode":"const VERIFY_CHECK_INTERVAL_MS = 50\nconst VERIFY_BLOCK_MS = 1_200\nconst PRODUCTION_HEARTBEAT_INTERVAL_MS = 2_000\nconst PRODUCTION_TIMEOUT_MS = 45_000\nconst PRODUCTION_CHECK_INTERVAL_MS = 5_000\nconst PRODUCTION_SAMPLE_MS = 30_000\nconst MAX_LAUNCH_ATTEMPTS = 3\nconst MIB = 1024 * 1024\nconst scriptPath = import.meta.filename\nconst repoRoot = path.resolve(import.meta.dirname, '..', '..')\nconst entryPath = path.join(repoRoot, 'out', 'main', 'main-thread-hang-watchdog-entry.js')\n\nfunction sleep(ms) {\n  return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nfunction forceGc() {\n  if (typeof global.gc !== 'function') {\n    throw new Error('Electron did not expose GC; keep --js-flags=--expose-gc in the harness')\n  }\n  global.gc()\n  global.gc()\n}\n\nfunction blockMainThread(ms) {\n  const startedAt = Date.now()\n  while (Date.now() - startedAt < ms) {\n    // Intentional synchronous stall.\n  }\n  return { startedAt, endedAt: Date.now() }\n}\n\nfunction readMarker(markerPath) {\n  try {\n    return JSON.parse(readFileSync(markerPath, 'utf8'))\n  } catch {\n    return null","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/config/scripts/hang-watchdog-memory-benchmark.mjs#L24-L60","documentation":"Thrown by forceGc() in the hang-watchdog memory benchmark when `global.gc` is not a function. V8 only exposes `global.gc` when the process is started with `--js-flags=--expose-gc`. The benchmark calls forceGc() to flush unreferenced objects before sampling RSS, so accurate memory deltas depend on a real GC. This is a harness-integrity guard: runTrial always launches Electron with `--js-flags=--expose-gc`, so hitting it means the flag was stripped or overridden.","triggerScenarios":"Calling forceGc() in a process launched without --expose-gc — e.g. running the script directly under plain Node, under Electron with a custom ELECTRON_RUN_AS_NODE invocation that drops the flag, or after an Electron version change that altered flag passthrough.","commonSituations":"Manually invoking the internal path (`ORCA_HANG_WATCHDOG_BENCH_INTERNAL=1`) without the flag, debugging by running `node config/scripts/hang-watchdog-memory-benchmark.mjs` directly, or an Electron upgrade that silently ignores unknown --js-flags.","solutions":["Run the benchmark through its public entry (runBenchmark), which spawns Electron with `--js-flags=--expose-gc` automatically.","If invoking the internal path manually, launch Electron with the flag: `electron --js-flags=--expose-gc <launcher>`.","Verify the flag took effect: `node --expose-gc -e 'console.log(typeof global.gc)'` should print `function`.","On Electron upgrades, confirm the flag is still honored by the bundled V8."],"exampleFix":"// before: internal path without the flag\nELECTRON_RUN_AS_NODE=1 node config/scripts/hang-watchdog-memory-benchmark.mjs\n// after: go through runBenchmark, which adds --expose-gc\nnode config/scripts/hang-watchdog-memory-benchmark.mjs --boundary child --trials 7","handlingStrategy":"validation","validationCode":"// Before relying on forceGc, confirm the flag took effect\nfunction assertGcExposed() {\n  if (typeof global.gc !== 'function') {\n    throw new Error('start with --js-flags=--expose-gc; global.gc is missing')\n  }\n}\nassertGcExposed()","typeGuard":"const isGcExposed = (): boolean => typeof global.gc === 'function'","tryCatchPattern":null,"preventionTips":["Always launch Electron with --js-flags=--expose-gc when running GC-sensitive benchmarks.","Prefer the public runBenchmark entry, which adds the flag automatically, over invoking the internal path directly.","After an Electron upgrade, re-verify the flag is honored by the bundled V8."],"tags":["benchmark","electron","v8-gc","memory","harness"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}