moeru-ai/airi · error
mutexAcquireTimeout must be a positive finite number
Error message
mutexAcquireTimeout must be a positive finite number
What it means
Thrown by initScreenCaptureForMain() when options.mutexAcquireTimeout is a number that is <= 0, not finite (Infinity/-Infinity), or NaN. The value is used to wrap the source-selection Mutex with withTimeout, so a non-positive or unbounded timeout would make acquisition semantics meaningless.
Source
Thrown at packages/electron-screen-capture/src/main/index.ts:113
let screenCaptureSourceMutexHandle: string | undefined
let setSourceMutexTimeoutHandle: NodeJS.Timeout | undefined
export function initScreenCaptureForMain(options: InitMainOptions = {}): void {
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.View on GitHub (pinned to 27111382b4)
Solutions
- Omit the option to accept the 5000ms default, or pass a positive finite number such as 5000.
- Coerce and validate config-sourced values: const t = Number(raw); if (!(t > 0 && Number.isFinite(t))) use default.
- Reject non-numeric config at the boundary instead of forwarding it to initScreenCaptureForMain.
Example fix
// before
initScreenCaptureForMain({ mutexAcquireTimeout: Number(process.env.SCREENCAP_TIMEOUT) })
// after
const raw = Number(process.env.SCREENCAP_TIMEOUT)
initScreenCaptureForMain({ mutexAcquireTimeout: Number.isFinite(raw) && raw > 0 ? raw : 5000 }) Defensive patterns
Strategy: validation
Validate before calling
function resolveMutexTimeout(raw: unknown): number {
const n = typeof raw === 'number' ? raw : Number(raw)
if (!(Number.isFinite(n) && n > 0)) return 5000 // default
return n
}
initScreenCaptureForMain({ mutexAcquireTimeout: resolveMutexTimeout(configValue) }) Type guard
function isPositiveFinite(n: unknown): n is number {
return typeof n === 'number' && Number.isFinite(n) && n > 0
} Prevention
- Coerce env/config timeouts with a positive-finite guard and a default.
- Document the unit (milliseconds) at the config boundary.
- Reject non-numeric config values early rather than forwarding.
When it happens
Trigger: Passing mutexAcquireTimeout: 0, a negative number, Infinity, or NaN in InitMainOptions; passing a value read from env/JSON config without coercion that ends up as NaN.
Common situations: Config file with mutexAcquireTimeout: 0 intending 'no wait'; env var parsed with Number('') producing 0; JSON config with a string that Number()-coerces to NaN; copy-paste of a default that was later changed.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- No active source selected
- Selected source did not provide a live video track
- 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@27111382b4 (2026-08-12).
Data as JSON: /api/errors/98fbd1310ae6a0fa.
Report an issue: GitHub.