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
- Call it exactly once from the main entry (e.g. inside app.whenReady()) and pass every option there
- Remove the duplicate call site — search for all initScreenCaptureForMain references
- 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
- Initialize main-process modules exactly once inside app.whenReady()
- Wrap every cross-module initializer in a single ensureX() helper and call only that
- In tests, reset module registry (vi.resetModules) between cases to re-arm one-time guards
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
- initScreenCaptureForWindow should only be called once per wi
- mutexAcquireTimeout must be a positive finite number
- initScreenCaptureForMain must be called before calling initS
- timeout must be a positive finite number
- Source with id ${request.sourceId} not found.
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/73e7b1579acb120a.
Report an issue: GitHub.