moeru-ai/airi · error · Error

No audio track available in the stream

Error message

No audio track available in the stream

What it means

Thrown by the beat-sync detector's Web environment path when `navigator.mediaDevices.getDisplayMedia` returns a stream with zero audio tracks. The detector needs the system/audio loopback to perform beat analysis, so an audio-less screen share is rejected immediately after the user grants the display.

Source

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

  const updateParameters = (params: Partial<AnalyserWorkletParameters>) => {
    analyser?.updateParameters(params)
  }

  const startScreenCapture = async () => start(async (ctx) => {
    switch (options.env) {
      case StageEnvironment.Web: {
        const stream = await navigator.mediaDevices.getDisplayMedia({
          audio: {
            echoCancellation: false,
            noiseSuppression: false,
            autoGainControl: false,
          },
          video: true,
        })

        if (stream.getAudioTracks().length === 0) {
          throw new Error('No audio track available in the stream')
        }

        stream.getAudioTracks().forEach((track) => {
          let stopCalled = false
          track.addEventListener('ended', () => {
            if (stopCalled)
              return
            stopCalled = true
            stop()
          })
        })

        const node = ctx.createMediaStreamSource(stream)
        stopSource = () => {
          stream.getTracks().forEach(track => track.stop())
        }

        return node

View on GitHub (pinned to 27111382b4)

Solutions

  1. Instruct the user to enable 'Share audio' / 'Share tab audio' in the getDisplayMedia picker.
  2. On macOS Chrome, note system-audio capture is limited; recommend a platform/browser that supports it, or accept that beat-sync may be unavailable.
  3. Catch the error and show a user-facing message explaining audio sharing is required for beat sync.
  4. Optionally re-prompt the user to re-share with audio enabled.

Example fix

// before
const stream = await navigator.mediaDevices.getDisplayMedia({ audio: true, video: true })
// (detector throws if no audio track)

// after
try {
  await startBeatSync()
} catch (e) {
  if (e.message === 'No audio track available in the stream') {
    showUser('Enable "Share audio" in the screen-share prompt to use Beat Sync.')
  } else throw e
}
Defensive patterns

Strategy: try-catch

Validate before calling

async function hasAudioTrack(stream: MediaStream): Promise<boolean> {
  return stream.getAudioTracks().length > 0
}

const stream = await navigator.mediaDevices.getDisplayMedia({ audio: true, video: true })
if (!await hasAudioTrack(stream)) {
  stream.getTracks().forEach(t => t.stop())
  throw new Error('Display share had no audio track. Enable "Share audio".')
}

Type guard

function streamHasAudio(stream: MediaStream): boolean {
  return stream.getAudioTracks().length > 0
}

Try / catch

try {
  await startBeatSync()
} catch (e) {
  if (e instanceof Error && e.message === 'No audio track available in the stream') {
    showUser('Enable "Share audio" in the screen-share prompt to use Beat Sync.')
    return
  }
  throw e
}

Prevention

When it happens

Trigger: The user picks a screen/window/tab in the getDisplayMedia picker but does not (or cannot) share audio — e.g. unchecks 'Share audio', shares a tab that has no audio, or uses a browser/OS combination that does not expose audio capture. The check `stream.getAudioTracks().length === 0` fires right after the share is granted.

Common situations: Browser/OS does not support system-audio capture (notably most configurations on macOS Chrome, or Firefox); user deselects 'Share audio' in the picker; sharing a tab or window with no audio output; permission prompts that only grant video.

Related errors


AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12). Data as JSON: /api/errors/bc590306965a3b2f. Report an issue: GitHub.