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

  1. In the preload script, expose the ipcRenderer: contextBridge.exposeInMainWorld('electron', { ipcRenderer: { send, on, invoke, ... } }).
  2. Pass the ipcRenderer explicitly: useElectronEventaContext(window.electron.ipcRenderer).
  3. Guard the composable call behind an isElectron check in shared components.
  4. 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

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


AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12). Data as JSON: /api/errors/d1b924681279cd53. Report an issue: GitHub.