different-ai/openwork · error

Microphone capture is unavailable in this runtime.

Error message

Microphone capture is unavailable in this runtime.

What it means

Before connecting the realtime session the panel checks that the runtime supports microphone capture via navigator.mediaDevices?.getUserMedia. In environments where the Media Devices API is unavailable (non-secure contexts, runtimes without media permissions), enabling audio input cannot work, so it throws.

Source

Thrown at apps/app/src/react-app/domains/session/voice/voice-panel.tsx:484

        requestRealtimeResponse(channel, false);
      } else {
        setRuntimeStatus(voiceRealtime.micMuted ? "muted" : "listening");
      }
      return;
    }
    if (type === "error") {
      voiceRealtime.responseInProgress = false;
      const error = readRecord(event, "error");
      const message = typeof error.message === "string" ? error.message : "Realtime returned an error.";
      addEntry("system", message, { error: true });
      setRuntimeStatus("error", message);
    }
  }, [addEntry, props.sessionId, requestRealtimeResponse, setRuntimeStatus]);

  const connectRealtime = useCallback(async (audioInput = true) => {
    const client = props.client;
    if (!client) throw new Error("OpenWork host connection is not ready.");
    if (audioInput && !navigator.mediaDevices?.getUserMedia) throw new Error("Microphone capture is unavailable in this runtime.");

    disconnectRealtime(true);
    setRuntimeStatus("connecting", "Minting Realtime session...");
    const sessionContext = await loadVoiceSessionContext(props.opencodeBaseUrl, props.openworkToken, props.sessionId);
    const realtimeSession = await client.createVoiceRealtimeSession({ sessionContext });

    const peer = new RTCPeerConnection();
    voiceRealtime.peer = peer;
    if (audioInput) {
      setRuntimeStatus("connecting", "Requesting microphone...");
      const macPermissionGranted = await requestMacMicrophoneAccess();
      if (!macPermissionGranted) throw new Error("macOS denied microphone access. Enable OpenWork in System Settings > Privacy & Security > Microphone, then restart OpenWork.");
      const stream = await navigator.mediaDevices.getUserMedia({
        audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true },
      });
      voiceRealtime.stream = stream;
      setMicDiagnostics(stream);
      for (const track of stream.getAudioTracks()) {

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Ensure the app is served over HTTPS or localhost (getUserMedia requires a secure context)
  2. Use the desktop app runtime where the media bridge is available, or grant the webview microphone permission (iframe allow="microphone")
  3. Connect with audioInput = false to use typed voice commands without a microphone
  4. Check navigator.mediaDevices existence in UI and hide/disable the mic option when absent

Example fix

// before
await connectRealtime(true);
// after
if (!navigator.mediaDevices?.getUserMedia) {
  await connectRealtime(false); // typed voice commands only
  return;
}
await connectRealtime(true);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!navigator.mediaDevices?.getUserMedia) {
  toast.error("Microphone capture is not supported here. Use the desktop app or HTTPS.");
  return;
}

Type guard

const supportsMicCapture = (): boolean =>
  typeof navigator !== "undefined" && !!navigator.mediaDevices?.getUserMedia;

Try / catch

try {
  await connectRealtime(true);
} catch (e) {
  if (e instanceof Error && e.message.includes("Microphone capture is unavailable")) {
    offerTypedVoiceFallback(); // connectRealtime(false)
  } else throw e;
}

Prevention

When it happens

Trigger: connectRealtime(audioInput = true) when navigator.mediaDevices is undefined or navigator.mediaDevices.getUserMedia is missing — e.g. the page is not served over HTTPS/localhost, or the embedded webview strips media APIs.

Common situations: OpenWork web UI accessed over plain http; running in a webview/iframe without allow="microphone"; Linux/Windows builds or CI runtimes lacking the media stack; user launched the app in a browser that blocks getUserMedia.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/8630361aa3f97a87. Report an issue: GitHub.