moeru-ai/airi · warning
setSourceMutex released for window due to timeout. Please ma
Error message
setSourceMutex released for window due to timeout. Please make sure to invoke screenCaptureResetSource when getDisplayMedia is completed.
What it means
When a renderer invokes screenCapture.setSource, main acquires setSourceMutex and arms a timeout (request.timeout, default 5000 ms) that force-releases the source if the renderer never calls screenCapture.resetSource with the returned handle. This warning means the safety net fired: the capture source was reset and the mutex released because getDisplayMedia completed (or died) without the matching reset call.
Source
Thrown at packages/electron-screen-capture/src/main/index.ts:260
throw new Error(`Source with id ${request.sourceId} not found.`)
}
callback({
video: source,
audio: options?.loopbackWithMute ? LoopbackAudioTypes.LoopbackWithMute : LoopbackAudioTypes.Loopback,
})
})
setSourceMutexTimeoutHandle = setTimeout(() => {
if (screenCaptureSourceMutexHandle !== handle)
return
resetScreenCaptureSource()
setSourceMutex.release()
log
.withFields({ windowId, windowTitle: tryWindowTitle(window, windowTitle) })
.warn(
`setSourceMutex released for window due to timeout. `
+ 'Please make sure to invoke screenCaptureResetSource when getDisplayMedia is completed.',
)
}, timeout ?? 5000)
return handle
}
catch (e) {
log
.withFields({ windowId, windowTitle: tryWindowTitle(window, windowTitle) })
.withError(e)
.error('screenCaptureSetSourceEx failed for window')
resetScreenCaptureSource()
setSourceMutex.release()
throw e
}
})View on GitHub (pinned to 677329427f)
Solutions
- Always call screenCapture.resetSource(mutexHandle) in a finally after getDisplayMedia resolves or rejects, using the handle returned by setSource
- Pass a larger timeout in the setSource request if capture setup legitimately exceeds 5000 ms
- Treat each occurrence as a leak detector: find the code path that stopped the stream without resetting
Example fix
// before
const handle = await screenCaptureInvoke.setSource({ sourceId })
const stream = await navigator.mediaDevices.getUserMedia({
video: { mandatory: { chromeMediaSource: 'desktop', chromeMediaSourceId: sourceId } },
})
// no reset on stop -> mutex times out after 5s
// after
const handle = await screenCaptureInvoke.setSource({ sourceId })
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: { mandatory: { chromeMediaSource: 'desktop', chromeMediaSourceId: sourceId } },
})
stream.getVideoTracks()[0].addEventListener('ended', () => screenCaptureInvoke.resetSource(handle))
return stream
}
catch (error) {
await screenCaptureInvoke.resetSource(handle)
throw error
} Defensive patterns
Strategy: try-catch
Try / catch
const handle = await screenCaptureInvoke.setSource({ sourceId, timeout: 10_000 })
try {
return await navigator.mediaDevices.getUserMedia({
video: { mandatory: { chromeMediaSource: 'desktop', chromeMediaSourceId: sourceId } },
})
}
finally {
// releases the mutex and disarms the timeout on every path
await screenCaptureInvoke.resetSource(handle)
} Prevention
- Always pair setSource with resetSource(handle) in try/finally
- Reset on stream 'ended' too, so user-initiated stops release the mutex immediately
- Pass an explicit timeout sized to your slowest real capture setup
When it happens
Trigger: Renderer calls setSource, gets a stream from getDisplayMedia, then stops the tracks without invoking screenCapture.resetSource(handle); or an error path between acquiring the stream and releasing; or the renderer crashes/navigates away mid-capture; or negotiation legitimately takes longer than the timeout (default 5s).
Common situations: A missing finally block around getDisplayMedia; early returns on permission denial that skip reset; slow machines where source enumeration exceeds 5s.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- timeout must be a positive finite number
- Source with id ${request.sourceId} not found.
- checkMacOSScreenCapturePermission is only available on macOS
- requestMacOSScreenCapturePermission is only available on mac
- initScreenCaptureForMain should only be called once
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/afc95b6d9cbf957c.
Report an issue: GitHub.