jamiepine/voicebox · error · Error

System audio capture is only available in the desktop app.

Error message

System audio capture is only available in the desktop app.

What it means

Thrown by webAudio.startSystemAudioCapture() — the web platform stub of PlatformAudio. The browser cannot capture system/desktop audio (no equivalent to the Tauri desktop loopback), so this method is an unconditional throw. The companion isSystemAudioSupported() returns false and listOutputDevices() returns []; these are the supported capability probes a caller must check first.

Source

Thrown at web/src/platform/audio.ts:9

import type { PlatformAudio, AudioDevice } from '@/platform/types';

export const webAudio: PlatformAudio = {
  async isSystemAudioSupported(): Promise<boolean> {
    return false; // System audio capture not supported in web
  },

  async startSystemAudioCapture(_maxDurationSecs: number): Promise<void> {
    throw new Error('System audio capture is only available in the desktop app.');
  },

  async stopSystemAudioCapture(): Promise<Blob> {
    throw new Error('System audio capture is only available in the desktop app.');
  },

  async listOutputDevices(): Promise<AudioDevice[]> {
    return []; // No native device routing in web
  },

  async playToDevices(_audioData: Uint8Array, _deviceIds: string[]): Promise<void> {
    throw new Error('Native audio device routing is only available in the desktop app.');
  },

  stopPlayback(): void {
    // No-op for web
  },
};

View on GitHub (pinned to 51f49dea19)

Solutions

  1. Gate the call behind isSystemAudioSupported() and disable the UI affordance when it returns false.
  2. Confirm the platform resolver injects the desktop audio implementation inside the Tauri build, not webAudio.
  3. Surface an in-app message directing the user to the desktop app for system-audio capture.
  4. Provide a web alternative (getUserMedia microphone capture) where system audio is not strictly required.

Example fix

// before
await platform.audio.startSystemAudioCapture(30);
// after
if (!(await platform.audio.isSystemAudioSupported())) {
  toast({ title: 'System audio capture needs the desktop app.' });
  return;
}
await platform.audio.startSystemAudioCapture(30);
Defensive patterns

Strategy: validation

Validate before calling

async function canCaptureSystemAudio(audio: PlatformAudio): Promise<boolean> {
  try {
    return await audio.isSystemAudioSupported();
  } catch {
    return false;
  }
}
// Guard every entry point:
if (!(await canCaptureSystemAudio(platform.audio))) {
  toast({ title: 'System audio capture needs the desktop app.' });
  return;
}

Try / catch

if (!(await platform.audio.isSystemAudioSupported())) {
  toast({ title: 'System audio capture is desktop-only.' });
  return;
}
try {
  await platform.audio.startSystemAudioCapture(30);
} catch (e) {
  toast({ title: 'Capture failed', description: (e as Error).message, variant: 'destructive' });
}

Prevention

When it happens

Trigger: Code path running on the web build calls startSystemAudioCapture(maxDurationSecs) directly. This happens when platform-agnostic code forgets to gate on isSystemAudioSupported() or when the wrong platform implementation is injected (web build used where desktop was expected).

Common situations: A feature developed against the Tauri desktop build is exercised in the web build. The platform PlatformAudio binding falls back to webAudio because the desktop module wasn't bundled. A user opens the web UI expecting system-audio capture that only exists in the desktop app.

Related errors


AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12). Data as JSON: /api/errors/71b00279c9123ab0. Report an issue: GitHub.