moeru-ai/airi · warning

initScreenCaptureForWindow should only be called once per wi

Error message

initScreenCaptureForWindow should only be called once per window

What it means

initScreenCaptureForWindow registers per-window IPC handlers (permissions, getSources, setSource/resetSource) keyed by the BrowserWindow object in an initializedWindows WeakSet. Calling it again with the same window logs this warning and returns; the previously created context and handlers stay active. It also throws outright if initScreenCaptureForMain has not run yet.

Source

Thrown at packages/electron-screen-capture/src/main/index.ts:197

  let log = useLogg('screen-capture').useGlobalConfig()
  if (options?.loggerOptions?.logLevel) {
    log = log.withLogLevelString((options?.loggerOptions?.logLevel ?? 'info') as LogLevelString)
  }
  if (options?.loggerOptions?.format) {
    log = log.withFormat((options?.loggerOptions?.format ?? 'plain') as Format)
  }

  const windowId = window.id
  const windowTitle = tryWindowTitle(window)

  log.withFields({ windowId, windowTitle: tryWindowTitle(window, windowTitle) }).debug(`init for window`)

  if (!initMainCalled) {
    // Throwing an error because this is unlikely to be recoverable.
    throw new Error('initScreenCaptureForMain must be called before calling initScreenCaptureForWindow')
  }
  if (initializedWindows.has(window)) {
    log.withFields({ windowId, windowTitle: tryWindowTitle(window, windowTitle) }).warn('initScreenCaptureForWindow should only be called once per window')
    return
  }

  initializedWindows.add(window)

  const { context } = createContext(ipcMain, window, { onlySameWindow: true })
  const session = sessionModule.defaultSession

  defineInvokeHandler(context, screenCapture.checkMacOSPermission, async () => checkMacOSScreenCapturePermission())
  defineInvokeHandler(context, screenCapture.requestMacOSPermission, async () => requestMacOSScreenCapturePermission())

  defineInvokeHandler(context, screenCapture.getSources, async (sourcesOptions) => {
    // NOTICE(@nekomeowww): In probability of 9/10, the window thumbnail is purely empty or black, sources printed and
    // nothing is returned from the desktopCapturer API.
    // NOTICE(@sumimakito): Not only thumbnail is empty, the appIcon could be empty as well with nothing returned.
    // REVIEW(@sumimakito): This has nothing to do with out side, probably related to Electron Bug, you can
    // read more here https://github.com/electron/electron/issues/44504
    const sources = await desktopCapturer.getSources(sourcesOptions)

View on GitHub (pinned to 677329427f)

Solutions

  1. Register each window once at creation time, in one place (the window factory)
  2. Audit all initScreenCaptureForWindow call sites and consolidate them
  3. Remember the guard is keyed by window object: a reloaded (same) window is intentionally skipped, so do not add a second call to 'refresh' handlers

Example fix

// before
function createMainWindow() {
  const win = new BrowserWindow({ ... })
  initScreenCaptureForWindow(win)
  win.webContents.on('did-finish-load', () => initScreenCaptureForWindow(win)) // duplicate
  return win
}

// after
function createMainWindow() {
  const win = new BrowserWindow({ ... })
  initScreenCaptureForWindow(win)
  return win
}
Defensive patterns

Strategy: validation

Validate before calling

const initialized = new WeakSet<BrowserWindow>()
export function setupWindowCapture(win: BrowserWindow) {
  if (initialized.has(win))
    return
  initScreenCaptureForWindow(win)
  initialized.add(win)
}

Prevention

When it happens

Trigger: Calling initScreenCaptureForWindow(window) twice for one BrowserWindow — e.g. calling it from both a window factory and a did-finish-load handler, or re-running setup code after an exception was already handled.

Common situations: Multiple modules each wiring screen capture for the same window; retry logic around window creation that accidentally re-invokes init; refactors that merged two call sites.

Related errors


AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18). Data as JSON: /api/errors/f1a0a6a351385f8c. Report an issue: GitHub.