moeru-ai/airi · error · Error
Electron ipcRenderer is not available. Pass it explicitly to
Error message
Electron ipcRenderer is not available. Pass it explicitly to useElectronEventaContext().
What it means
Thrown by resolveIpcRenderer() in @proj-airi/electron-vueuse when no ipcRenderer is passed explicitly and globalThis.window.electron.ipcRenderer is absent. The shared eventa context is created lazily from an ipcRenderer, so without one the context cannot be built. Common in preload-bridge setups where window.electron is not injected.
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 27111382b4)
Solutions
- In the preload script, expose the ipcRenderer: contextBridge.exposeInMainWorld('electron', { ipcRenderer: { send, on, invoke, ... } }).
- Pass the ipcRenderer explicitly: useElectronEventaContext(window.electron.ipcRenderer).
- Guard the composable call behind an isElectron check in shared components.
- In tests, mock globalThis.window.electron.ipcRenderer before mounting.
Example fix
// before (preload)
contextBridge.exposeInMainWorld('api', { foo })
// renderer: useElectronEventaContext() throws
// after
contextBridge.exposeInMainWorld('electron', { ipcRenderer: { send: ipcRenderer.send.bind(ipcRenderer), on: ipcRenderer.on.bind(ipcRenderer), invoke: ipcRenderer.invoke.bind(ipcRenderer) } }) Defensive patterns
Strategy: validation
Validate before calling
function resolveIpc(): IpcRendererLike {
const explicit = window.electron?.ipcRenderer
if (!explicit) throw new Error('ipcRenderer missing; check the preload bridge exposes window.electron.ipcRenderer')
return explicit
}
useElectronEventaContext(resolveIpc()) Type guard
function hasIpcRenderer(g: unknown): g is { electron: { ipcRenderer: object } } {
return !!g && typeof g === 'object' && 'electron' in (g as any) && !!(g as any).electron?.ipcRenderer
} Try / catch
try {
useElectronEventaContext()
} catch (error) {
if (error instanceof Error && error.message.includes('ipcRenderer is not available')) {
// fall back to a no-op context or show 'Electron bridge missing' UI
} else throw error
} Prevention
- Expose window.electron.ipcRenderer via contextBridge.exposeInMainWorld in the preload.
- Guard shared components with an isElectron check before calling the composable.
- Mock globalThis.window.electron.ipcRenderer in renderer unit tests.
When it happens
Trigger: Calling useElectronEventaContext() or getElectronEventaContext() with no argument in a renderer where the preload script did not expose window.electron.ipcRenderer; calling it outside an Electron renderer (e.g. in a plain web/Vite dev context, SSR, or a Node test).
Common situations: Preload script uses contextIsolation without exposeInMainWorld('electron', { ipcRenderer }); running component tests in jsdom/happy-dom without mocking the bridge; a Vue component shared between web and Electron that calls the composable unconditionally; Electron upgrade that changed the bridge shape.
Related errors
- Failed to apply server channel configuration
- Invalid Godot stage scene input payload.
- Godot stage is not running.
- Godot stage bridge is not connected.
- EXTENSION_ASSET_METHOD_NOT_ALLOWED
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/d1b924681279cd53.
Report an issue: GitHub.