moeru-ai/airi · error · Error
Electron ipcRenderer is not available. Pass it explicitly…
Error message
Electron ipcRenderer is not available. Pass it explicitly to useElectronEventaContext().
What it means
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.
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.
Example fix
// before const ctx = useElectronEventaContext() // module scope; throws in web/tests // after const hasBridge = typeof window !== 'undefined' && !!window.electron?.ipcRenderer const ctx = hasBridge ? useElectronEventaContext() : undefined // use a web eventa context (e.g. BroadcastChannel) instead
Defensive patterns
Strategy: validation
Validate before calling
const ipc = (globalThis as any).window?.electron?.ipcRenderer
if (!ipc && !import.meta.env.ELECTRON) {
// running outside Electron: use a web transport instead of useElectronEventaContext()
} Type guard
function hasExposedIpcRenderer(g: typeof globalThis): g is typeof globalThis & { window: { electron: { ipcRenderer: object } } } {
const w = (g as { window?: unknown }).window
return typeof w === 'object' && w !== null
&& typeof (w as any).electron?.ipcRenderer === 'object'
} Try / catch
try {
const ctx = useElectronEventaContext()
} catch {
// no preload bridge: fall back to a non-Electron eventa context or fail loudly in dev
} Prevention
- 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).
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Electron IPC is not available in this renderer context
- Extension folder import is available only from the…
- Extension import plan is not available to this renderer.
- initScreenCaptureForMain must be called before calling…
- initScreenCaptureForWindow should only be called once per…
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/d1b924681279cd53.
Report an issue: GitHub.
Appendix: source
Thrown at packages/electron-vueuse/src/composables/use-electron-eventa-context.ts:20
import type { ShallowRef } from 'vue'
import { defineInvoke } from '@moeru/eventa'
import { createContext } from '@moeru/eventa/adapters/electron/renderer'
import { shallowRef } from 'vue'
type EventaContext = ReturnType<typeof createContext>['context']
type IpcRendererLike = Parameters<typeof createContext>[0]
let sharedContext: EventaContext | undefined
function resolveIpcRenderer(ipcRenderer?: IpcRendererLike): IpcRendererLike {
if (ipcRenderer) {
return ipcRenderer
}
const globalIpcRenderer = (globalThis as { window?: { electron?: { ipcRenderer?: IpcRendererLike } } }).window?.electron?.ipcRenderer
if (!globalIpcRenderer) {
throw new Error('Electron ipcRenderer is not available. Pass it explicitly to useElectronEventaContext().')
}
return globalIpcRenderer
}
export function getElectronEventaContext(ipcRenderer?: IpcRendererLike): EventaContext {
sharedContext ??= createContext(resolveIpcRenderer(ipcRenderer)).context
return sharedContext
}
export function useElectronEventaContext(ipcRenderer?: IpcRendererLike): ShallowRef<EventaContext> {
return shallowRef(getElectronEventaContext(ipcRenderer))
}
export function useElectronEventaInvoke<Res, Req = undefined, ResErr = Error, ReqErr = Error>(invoke: InvokeEventa<Res, Req, ResErr, ReqErr>, context?: EventaContext) {
return defineInvoke(context ?? getElectronEventaContext(), invoke)
}
View on GitHub (pinned to 677329427f)