{"record":{"id":"ef786be4296f9cc6","repo":"moeru-ai/airi","slug":"selected-source-did-not-provide-a-live-video-track","errorCode":null,"errorMessage":"Selected source did not provide a live video track","messagePattern":"Selected source did not provide a live video track","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/stage-tamagotchi/src/renderer/composables/use-vision-screen-capture.ts","lineNumber":136,"sourceCode":"  }\n\n  async function startStream() {\n    const sourceId = activeSourceId.value\n    if (!sourceId)\n      throw new Error('No active source selected')\n\n    if (isActiveStream(activeStream.value) && activeStreamSourceId.value === sourceId)\n      return activeStream.value!\n\n    clearActiveStream()\n\n    const stream = await selectWithSource(\n      () => sourceId,\n      async () => await navigator.mediaDevices.getDisplayMedia({ video: true, audio: false }),\n    )\n    if (!isActiveStream(stream)) {\n      stream.getTracks().forEach(track => track.stop())\n      throw new Error('Selected source did not provide a live video track')\n    }\n\n    activeStream.value = stream\n    activeStreamSourceId.value = sourceId\n    attachStreamLifecycle(stream, sourceId)\n\n    return stream\n  }\n\n  function stopStream() {\n    clearActiveStream()\n  }\n\n  function cleanup() {\n    stopStream()\n    revokeSourceObjectUrls(sources.value)\n  }\n","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/apps/stage-tamagotchi/src/renderer/composables/use-vision-screen-capture.ts#L118-L154","documentation":"Thrown after getDisplayMedia resolves but the returned stream has no live video track (isActiveStream returned false). The user granted a stream, but it carries no video track — e.g. they shared audio-only, or the track ended immediately. The code stops the tracks then throws.","triggerScenarios":"User selected a source that exposes only audio; the video track was ended by the OS/source between grant and inspection; source (e.g. a closed window) became unavailable instantly; getDisplayMedia returned a stream whose video track is muted/ended.","commonSituations":"User picked an audio-only share target; the shared window was closed mid-handshake; permissions allow audio but not video; Electron/Chromium version quirk returning ended tracks.","solutions":["Retry the capture and instruct the user to pick a source with video (a screen/window, not an audio-only input).","Confirm the app requested `{ video: true }` (it does) and that OS screen-recording permission covers video.","Handle the ended-track case by re-prompting rather than throwing if the track state is 'ended'.","On macOS, verify Screen Recording permission in System Settings > Privacy & Security."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function streamHasLiveVideoTrack(stream: MediaStream): boolean {\n  return stream.getVideoTracks().some(t => t.readyState === 'live' && t.enabled)\n}","typeGuard":"function isActiveStream(stream: MediaStream | null | undefined): stream is MediaStream {\n  return !!stream && stream.getVideoTracks().some(t => t.readyState === 'live')\n}","tryCatchPattern":"try {\n  await startStream()\n} catch (e) {\n  if (e instanceof Error && e.message.includes('live video track')) {\n    // re-prompt the user to pick a source with video\n  } else throw e\n}","preventionTips":["Verify OS screen-recording permission covers video before prompting.","Filter source enumeration to video-capable sources only.","Re-prompt on ended tracks rather than throwing hard."],"tags":["screen-capture","permissions","media","electron"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}