moeru-ai/airi · warning

initScreenCaptureForMain should only be called once

Error message

initScreenCaptureForMain should only be called once

What it means

initScreenCaptureForMain is a one-time Electron main-process initializer: it validates options, sets the source mutex, and reads Chromium feature switches. A module-level initMainCalled flag makes a second call log this warning and return early, so any options passed to the second call are silently ignored.

Source

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

  const {
    forceCoreAudioTap = false,
    mutexAcquireTimeout = 5000,
  } = options

  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)
  }

  if (mutexAcquireTimeout <= 0 || !Number.isFinite(mutexAcquireTimeout) || Number.isNaN(mutexAcquireTimeout)) {
    throw new Error('mutexAcquireTimeout must be a positive finite number')
  }

  if (initMainCalled) {
    log.warn('initScreenCaptureForMain should only be called once')
    return
  }
  initMainCalled = true
  setSourceMutex = withTimeout(new Mutex(), mutexAcquireTimeout)

  // Get other enabled features from the command line.
  const otherEnabledFeatures = app.commandLine.getSwitchValue(featureSwitchKey)?.split(',')

  // Remove the switch if it exists.
  if (app.commandLine.hasSwitch(featureSwitchKey)) {
    app.commandLine.removeSwitch(featureSwitchKey)
  }

  // Add the feature flags to the command line with any other user-enabled features concatenated.
  const currentFeatureFlags = buildFeatureFlags({
    otherEnabledFeatures,
    forceCoreAudioTap,
  })

View on GitHub (pinned to 677329427f)

Solutions

  1. Call it exactly once from the main entry (e.g. inside app.whenReady()) and pass every option there
  2. Remove the duplicate call site — search for all initScreenCaptureForMain references
  3. In tests, reset module state between cases (vi.resetModules) or extract the init into a shared setup helper

Example fix

// before (two call sites)
await app.whenReady()
initScreenCaptureForMain({ loggerOptions: { logLevel: 'debug' } })
// ... later, in another module
initScreenCaptureForMain({})

// after (single call site, all options)
await app.whenReady()
initScreenCaptureForMain({ loggerOptions: { logLevel: 'debug' }, loopbackWithMute: true })
Defensive patterns

Strategy: validation

Validate before calling

// single-flight guard in your own code
let screenCaptureMainInitialized = false
export async function ensureScreenCaptureMain() {
  if (screenCaptureMainInitialized)
    return
  initScreenCaptureForMain({ loggerOptions: { logLevel: 'info' } })
  screenCaptureMainInitialized = true
}

Prevention

When it happens

Trigger: Calling initScreenCaptureForMain twice — e.g. from app bootstrap and again from window-creation code, in tests that init per test case, or after HMR re-runs a main-process bundle.

Common situations: Refactoring moves the init call and leaves the old one in place; tests importing the module multiple times without resetting module state; a plugin and the app both trying to own initialization.

Related errors


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