{"record":{"id":"d1b924681279cd53","repo":"moeru-ai/airi","slug":"electron-ipcrenderer-is-not-available-pass-it-exp","errorCode":null,"errorMessage":"Electron ipcRenderer is not available. Pass it explicitly to useElectronEventaContext().","messagePattern":"Electron ipcRenderer is not available\\. Pass it explicitly to useElectronEventaContext\\(\\)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/electron-vueuse/src/composables/use-electron-eventa-context.ts","lineNumber":20,"sourceCode":"import type { ShallowRef } from 'vue'\n\nimport { defineInvoke } from '@moeru/eventa'\nimport { createContext } from '@moeru/eventa/adapters/electron/renderer'\nimport { shallowRef } from 'vue'\n\ntype EventaContext = ReturnType<typeof createContext>['context']\ntype IpcRendererLike = Parameters<typeof createContext>[0]\n\nlet sharedContext: EventaContext | undefined\n\nfunction resolveIpcRenderer(ipcRenderer?: IpcRendererLike): IpcRendererLike {\n  if (ipcRenderer) {\n    return ipcRenderer\n  }\n\n  const globalIpcRenderer = (globalThis as { window?: { electron?: { ipcRenderer?: IpcRendererLike } } }).window?.electron?.ipcRenderer\n  if (!globalIpcRenderer) {\n    throw new Error('Electron ipcRenderer is not available. Pass it explicitly to useElectronEventaContext().')\n  }\n\n  return globalIpcRenderer\n}\n\nexport function getElectronEventaContext(ipcRenderer?: IpcRendererLike): EventaContext {\n  sharedContext ??= createContext(resolveIpcRenderer(ipcRenderer)).context\n  return sharedContext\n}\n\nexport function useElectronEventaContext(ipcRenderer?: IpcRendererLike): ShallowRef<EventaContext> {\n  return shallowRef(getElectronEventaContext(ipcRenderer))\n}\n\nexport function useElectronEventaInvoke<Res, Req = undefined, ResErr = Error, ReqErr = Error>(invoke: InvokeEventa<Res, Req, ResErr, ReqErr>, context?: EventaContext) {\n  return defineInvoke(context ?? getElectronEventaContext(), invoke)\n}\n","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/electron-vueuse/src/composables/use-electron-eventa-context.ts#L2-L38","documentation":"useElectronEventaContext()/getElectronEventaContext() in packages/electron-vueuse builds an eventa context over Electron IPC. It first uses an explicitly passed ipcRenderer, otherwise falls back to window.electron.ipcRenderer, the conventional bridge a contextBridge preload script exposes under contextIsolation. If neither exists it throws this error, meaning the code is running outside an Electron renderer with a properly configured preload (plain web app, SSR, test environment, or a preload that did not expose ipcRenderer). The shared context is memoized once, so the first successful resolution wins for the whole app.","triggerScenarios":"Calling useElectronEventaContext() with no argument in a Vue app running in a plain browser tab or in vitest/happy-dom; running in an Electron renderer whose preload does not do contextBridge.exposeInMainWorld('electron', { ipcRenderer }); preload disabled via sandbox/contextIsolation misconfiguration; module evaluated during SSR where window.electron does not exist.","commonSituations":"A shared composable package consumed by both stage-web (browser) and stage-tamagotchi (Electron) — the browser build hits it; a refactor moved eventa context creation to module top-level so it evaluates in tests; the preload script was renamed or the exposeInMainWorld key changed.","solutions":["Ensure the Electron preload exposes the bridge: contextBridge.exposeInMainWorld('electron', { ipcRenderer }) and that this preload is attached in the BrowserWindow webPreferences.","If you are in an Electron renderer with a non-standard bridge, pass ipcRenderer explicitly: useElectronEventaContext(window.myIpc).","If the code can run outside Electron (web build, tests), guard the call with a window.electron?.ipcRenderer check and use a different transport/context on that branch.","Defer useElectronEventaContext() to component setup / client-only code instead of module top-level so SSR and test bundles never evaluate it."],"exampleFix":"// before\nconst ctx = useElectronEventaContext() // module scope; throws in web/tests\n\n// after\nconst hasBridge = typeof window !== 'undefined' && !!window.electron?.ipcRenderer\nconst ctx = hasBridge\n  ? useElectronEventaContext()\n  : undefined // use a web eventa context (e.g. BroadcastChannel) instead","handlingStrategy":"validation","validationCode":"const ipc = (globalThis as any).window?.electron?.ipcRenderer\nif (!ipc && !import.meta.env.ELECTRON) {\n  // running outside Electron: use a web transport instead of useElectronEventaContext()\n}","typeGuard":"function hasExposedIpcRenderer(g: typeof globalThis): g is typeof globalThis & { window: { electron: { ipcRenderer: object } } } {\n  const w = (g as { window?: unknown }).window\n  return typeof w === 'object' && w !== null\n    && typeof (w as any).electron?.ipcRenderer === 'object'\n}","tryCatchPattern":"try {\n  const ctx = useElectronEventaContext()\n} catch {\n  // no preload bridge: fall back to a non-Electron eventa context or fail loudly in dev\n}","preventionTips":["Expose ipcRenderer in the preload via contextBridge.exposeInMainWorld('electron', { ipcRenderer }) and assert it in dev builds.","Never call useElectronEventaContext() at module top level; call it in setup/client-only code so web builds and tests never evaluate it.","For unit tests, pass an explicit ipcRenderer stub: useElectronEventaContext(fakeIpcRenderer)."],"tags":["electron","preload","ipc","vue","eventa","context-isolation"],"backgroundTag":"electron-preload-bridge-missing","analyzedSha":"677329427f32468c74b17f3ec47eeca4e05bec65","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}