moeru-ai/airi · error · Error

No screen source available

Error message

No screen source available

What it means

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.

Solutions

  1. On macOS, grant Screen Recording permission to the stage-tamagotchi/Electron app in System Settings and relaunch
  2. Widen sourcesOptions.types to include 'window' and pick from the combined list
  3. Detect this error and show an in-app prompt directing the user to OS privacy settings

Example fix

// before
{ sourcesOptions: { types: ['screen'] } }
// after
{ sourcesOptions: { types: ['screen', 'window'] } }
Defensive patterns

Strategy: fallback

Validate before calling

const sources = await desktopCapturer.getSources({ types: ['screen', 'window'] })
if (sources.length === 0) {
  // no capturable surface: OS permission or headless environment
  showPermissionHint()
  return
}

Type guard

const hasScreenSources = (sources: { id: string }[]): boolean => sources.length > 0

Try / catch

try {
  await startBeatSync({ env: StageEnvironment.Tamagotchi })
}
catch (error) {
  if (error instanceof Error && error.message === 'No screen source available') {
    promptForScreenRecordingPermission() // macOS System Settings hint
    return
  }
  throw error
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18). Data as JSON: /api/errors/52dec31d13458690. Report an issue: GitHub.

Appendix: source

Thrown at packages/stage-shared/src/beat-sync/detector.ts:170

        stopSource = () => {
          stream.getTracks().forEach(track => track.stop())
        }

        return node
      }
      case StageEnvironment.Tamagotchi: {
        if (!isElectronWindow(window)) {
          throw new Error(`Electron window is required for this environment: ${options.env}`)
        }

        // FIXME(Makito): Will refactor later
        const { createContext } = await import('@moeru/eventa/adapters/electron/renderer')
        const { selectWithSource } = setupElectronScreenCapture(createContext(window.electron.ipcRenderer).context)

        const stream = await selectWithSource(
          (sources: SerializableDesktopCapturerSource[]) => {
            if (sources.length === 0)
              throw new Error('No screen source available')
            return sources[0].id
          },
          async () => await navigator.mediaDevices.getDisplayMedia({
            video: true,
            audio: true,
          }),
          { sourcesOptions: { types: ['screen'] } },
        )

        const videoTracks = stream.getVideoTracks()

        videoTracks.forEach((track: MediaStreamTrack) => {
          track.stop()
          stream.removeTrack(track)
        })

        const node = ctx.createMediaStreamSource(stream)
        stopSource = () => {

View on GitHub (pinned to 677329427f)