{"record":{"id":"52dec31d13458690","repo":"moeru-ai/airi","slug":"no-screen-source-available","errorCode":null,"errorMessage":"No screen source available","messagePattern":"No screen source available","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stage-shared/src/beat-sync/detector.ts","lineNumber":170,"sourceCode":"        stopSource = () => {\n          stream.getTracks().forEach(track => track.stop())\n        }\n\n        return node\n      }\n      case StageEnvironment.Tamagotchi: {\n        if (!isElectronWindow(window)) {\n          throw new Error(`Electron window is required for this environment: ${options.env}`)\n        }\n\n        // FIXME(Makito): Will refactor later\n        const { createContext } = await import('@moeru/eventa/adapters/electron/renderer')\n        const { selectWithSource } = setupElectronScreenCapture(createContext(window.electron.ipcRenderer).context)\n\n        const stream = await selectWithSource(\n          (sources: SerializableDesktopCapturerSource[]) => {\n            if (sources.length === 0)\n              throw new Error('No screen source available')\n            return sources[0].id\n          },\n          async () => await navigator.mediaDevices.getDisplayMedia({\n            video: true,\n            audio: true,\n          }),\n          { sourcesOptions: { types: ['screen'] } },\n        )\n\n        const videoTracks = stream.getVideoTracks()\n\n        videoTracks.forEach((track: MediaStreamTrack) => {\n          track.stop()\n          stream.removeTrack(track)\n        })\n\n        const node = ctx.createMediaStreamSource(stream)\n        stopSource = () => {","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/stage-shared/src/beat-sync/detector.ts#L152-L188","documentation":"In the Tamagotchi (Electron) branch of the beat-sync detector (detector.ts:170), desktopCapturer sources of type 'screen' are listed and the first one is used to drive getDisplayMedia. If the callback receives an empty sources array, this error is thrown: Electron found zero screen-type capture sources, so there is no screen id to request.","triggerScenarios":"macOS screen-recording permission denied to the Electron app, running on a headless/virtual display setup, or { types: ['screen'] } filtering out everything because only windows are reported.","commonSituations":"First run on macOS before Screen Recording permission is granted (sources come back empty), CI/headless machines, or systems where only window sources are enumerated.","solutions":["On macOS, grant Screen Recording permission to the stage-tamagotchi/Electron app in System Settings and relaunch","Widen sourcesOptions.types to include 'window' and pick from the combined list","Detect this error and show an in-app prompt directing the user to OS privacy settings"],"exampleFix":"// before\n{ sourcesOptions: { types: ['screen'] } }\n// after\n{ sourcesOptions: { types: ['screen', 'window'] } }","handlingStrategy":"fallback","validationCode":"const sources = await desktopCapturer.getSources({ types: ['screen', 'window'] })\nif (sources.length === 0) {\n  // no capturable surface: OS permission or headless environment\n  showPermissionHint()\n  return\n}","typeGuard":"const hasScreenSources = (sources: { id: string }[]): boolean => sources.length > 0","tryCatchPattern":"try {\n  await startBeatSync({ env: StageEnvironment.Tamagotchi })\n}\ncatch (error) {\n  if (error instanceof Error && error.message === 'No screen source available') {\n    promptForScreenRecordingPermission() // macOS System Settings hint\n    return\n  }\n  throw error\n}","preventionTips":["Request/grant macOS Screen Recording permission before first capture","Include 'window' in sourcesOptions.types so window-only setups still work","Never assume a display source exists in CI or headless runs"],"tags":["electron","desktop-capturer","permissions","beat-sync","macos"],"backgroundTag":"no-screen-capture-sources","analyzedSha":"677329427f32468c74b17f3ec47eeca4e05bec65","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}