moeru-ai/airi · error · Error
No screen source available
Error message
No screen source available
What it means
Thrown by the beat-sync detector's Tamagotchi (Electron) path when `desktopCapturer` returns an empty source list, i.e. no screen sources were available to select. The selector callback receives `sources` and throws if its length is 0 before returning a source id, so the capture cannot proceed.
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 27111382b4)
Solutions
- Grant Screen Recording permission to the Electron app in macOS System Settings (Privacy & Security), then restart the app.
- In headless/CI environments, provide a virtual display (xvfb) or skip beat-sync entirely.
- Catch the error and surface a user-facing message guiding the user to enable screen-capture permission.
- Confirm `types: ['screen']` matches available sources; broaden to include 'window' if appropriate for the use case.
Example fix
// before
const stream = await selectWithSource(sources => {
if (sources.length === 0) throw new Error('No screen source available')
return sources[0].id
}, ...)
// after
try {
await startBeatSync()
} catch (e) {
if (e.message === 'No screen source available') {
showUser('Grant screen-recording permission to this app, then try again.')
} else throw e
} Defensive patterns
Strategy: try-catch
Validate before calling
import { desktopCapturer } from 'electron'
async function hasScreenSource(): Promise<boolean> {
const sources = await desktopCapturer.getSources({ types: ['screen'] })
return sources.length > 0
}
if (!await hasScreenSource()) {
throw new Error('No screen source available. Grant screen-recording permission.')
} Type guard
function hasScreenSources(sources: unknown[]): boolean {
return Array.isArray(sources) && sources.length > 0
} Try / catch
try {
await startBeatSync()
} catch (e) {
if (e instanceof Error && e.message === 'No screen source available') {
showUser('Grant screen-recording permission to this app in System Settings, then restart.')
return
}
throw e
} Prevention
- On macOS, grant Screen Recording permission to the Electron app and restart it.
- In headless/CI, provide a virtual display (xvfb) or skip beat sync.
- Catch the error and guide the user to the relevant OS privacy setting.
- Confirm a display is attached and not sleeping before invoking capture.
When it happens
Trigger: Running beat sync in the Electron (stage-tamagotchi) environment where `desktopCapturer.fetchSources({ types: ['screen'] })` yields no sources. This can happen when screen-recording permissions are denied, when running in a headless/sandboxed context with no displays, or when the OS blocks enumeration.
Common situations: macOS Screen Recording permission not granted to the Electron app; running in CI/headless without a virtual display; kiosk/lockdown policies that block screen enumeration; display sleep or no attached monitors.
Related errors
- Selected source did not provide a live video track
- Source with id ${request.sourceId} not found.
- No audio track available in the stream
- Invalid remote debug port: ${env.APP_REMOTE_DEBUG_PORT}
- Token exchange failed (${response.status}): ${text}
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/52dec31d13458690.
Report an issue: GitHub.