{"record":{"id":"f1a107790320ffc7","repo":"moeru-ai/airi","slug":"source-with-id-request-sourceid-not-found","errorCode":null,"errorMessage":"Source with id ${request.sourceId} not found.","messagePattern":"Source with id (.+?) not found\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/electron-screen-capture/src/main/index.ts","lineNumber":242,"sourceCode":"    const { timeout } = request\n    if (typeof timeout === 'number' && (timeout <= 0 || !Number.isFinite(timeout) || Number.isNaN(timeout))) {\n      throw new Error('timeout must be a positive finite number')\n    }\n\n    await setSourceMutex.acquire()\n    log.withFields({ windowId, windowTitle: tryWindowTitle(window, windowTitle) }).debug('setSourceMutex acquired')\n\n    clearTimeout(setSourceMutexTimeoutHandle)\n    const handle = nanoid()\n    setSourceMutexTimeoutHandle = undefined\n    screenCaptureSourceMutexHandle = handle\n\n    try {\n      session.setDisplayMediaRequestHandler(async (_request, callback) => {\n        const sources = await desktopCapturer.getSources(request.options)\n        const source = sources.find(source => source.id === request.sourceId)\n        if (!source) {\n          throw new Error(`Source with id ${request.sourceId} not found.`)\n        }\n\n        callback({\n          video: source,\n          audio: options?.loopbackWithMute ? LoopbackAudioTypes.LoopbackWithMute : LoopbackAudioTypes.Loopback,\n        })\n      })\n\n      setSourceMutexTimeoutHandle = setTimeout(() => {\n        if (screenCaptureSourceMutexHandle !== handle)\n          return\n\n        resetScreenCaptureSource()\n        setSourceMutex.release()\n\n        log\n          .withFields({ windowId, windowTitle: tryWindowTitle(window, windowTitle) })\n          .warn(","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/packages/electron-screen-capture/src/main/index.ts#L224-L260","documentation":"Thrown inside the session.setDisplayMediaRequestHandler callback when desktopCapturer.getSources(request.options) returns no source whose id equals request.sourceId. The handler was registered by screenCapture.setSource; it looks up the previously-enumerated source to feed back into the display-media callback.","triggerScenarios":"Renderer calls setSource with a sourceId that is stale (the source list changed between enumeration and selection), belongs to a different session, or is malformed; request.options filters (types/types) exclude the source so it is not returned.","commonSituations":"Source disconnected between listing and picking (display unplugged, app closed); source list cached too long in the renderer; types filter mismatch (e.g. requesting a 'window' id with types: ['screen']); cross-platform source id format differences.","solutions":["Re-fetch sources via getSources immediately before calling setSource and use the fresh id.","Match request.options types to the source type you are selecting (screen vs window).","Handle the error in the renderer and fall back to letting the user re-pick a source.","Avoid caching source ids across long periods or across display-configuration changes."],"exampleFix":"// before\ninvoke(setSource, { sourceId: cachedId, options: { types: ['screen'] } })\n// after\nconst sources = await invoke(getSources, { types: ['screen'] })\nconst match = sources.find(s => s.id === cachedId) ?? sources[0]\nif (!match) throw new Error('no source')\ninvoke(setSource, { sourceId: match.id, options: { types: ['screen'] } })","handlingStrategy":"validation","validationCode":"// Re-fetch and confirm the source id is current before calling setSource\nconst sources = await invoke(screenCapture.getSources, request.options)\nif (!sources.some(s => s.id === request.sourceId)) {\n  throw new Error('sourceId is stale; pick a fresh source from getSources()')\n}","typeGuard":"function isKnownSource(id: string, sources: { id: string }[]): boolean {\n  return sources.some(s => s.id === id)\n}","tryCatchPattern":"try {\n  await invoke(screenCapture.setSource, { sourceId, options })\n} catch (error) {\n  if (error instanceof Error && /Source with id .* not found/.test(error.message)) {\n    // refresh and retry once with a fresh source\n    const fresh = await invoke(screenCapture.getSources, options)\n    await invoke(screenCapture.setSource, { sourceId: fresh[0].id, options })\n  } else throw error\n}","preventionTips":["Never cache source ids longer than the display-configuration lifetime.","Match request.options.types to the source category you are selecting.","Refresh the source list right before letting the user pick."],"tags":["electron","screen-capture","desktop-capturer","stale-reference"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}