siyuan-note/siyuan · error · Error

AudioContext is not supported

Error message

AudioContext is not supported

What it means

RecordMedia constructor checks that either the global AudioContext or the legacy webkitAudioContext exists; if neither is defined it throws 'AudioContext is not supported'. This is a hard environment feature-detect failure for the Web Audio API before any recording can begin.

Source

Thrown at app/src/protyle/util/RecordMedia.ts:95

    private worker: Worker;
    private chunks: ArrayBuffer[] = [];
    private readyPromise: Promise<void>;
    private resolveReady: () => void;
    private rejectReady: (error: Error) => void;
    private stopPromise: Promise<Blob>;
    private resolveStop: (blob: Blob) => void;
    private rejectStop: (error: Error) => void;
    private disposed = false;
    private failure: Error;
    private readonly handleTrackEnded = () => {
        this.handleWorkerError(new RecordMediaInputEndedError());
    };

    constructor(mediaStream: MediaStream) {
        this.mediaStream = mediaStream;
        const AudioContextConstructor = typeof AudioContext !== "undefined" ? AudioContext : webkitAudioContext;
        if (!AudioContextConstructor) {
            throw new Error("AudioContext is not supported");
        }

        const contextOptions = {} as AudioContextOptions & { sinkId?: { type: "none" } };
        if ((isInAndroid() || isInHarmony()) && "setSinkId" in AudioContextConstructor.prototype) {
            contextOptions.sinkId = {type: "none"};
        }
        this.context = new AudioContextConstructor(contextOptions);
        if (!this.context.audioWorklet || typeof AudioWorkletNode === "undefined") {
            throw new Error("AudioWorklet is not supported");
        }
        this.audioInput = this.context.createMediaStreamSource(mediaStream);
        this.mediaStream.getAudioTracks().forEach((track) => {
            track.addEventListener("ended", this.handleTrackEnded);
        });
    }

    public async startRecording() {
        if (this.disposed) {

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Feature-detect AudioContext/webkitAudioContext before instantiating RecordMedia and disable the record button if missing.
  2. Ensure the page is served in a secure context (https or localhost) so the browser does not gate Web Audio.
  3. Update the host browser/webview to a version that ships Web Audio.
  4. In tests, polyfill AudioContext (standardized-audio-context mock).

Example fix

// before
const rec = new RecordMedia(stream);
// after
const Ctor = typeof AudioContext !== 'undefined' ? AudioContext : (window as any).webkitAudioContext;
if (!Ctor) { showMessage('Audio recording is not supported in this browser'); return; }
const rec = new RecordMedia(stream);
Defensive patterns

Strategy: type-guard

Validate before calling

const Ctor = typeof AudioContext !== 'undefined' ? AudioContext : (window as any).webkitAudioContext;
if (!Ctor) { disableRecordButton(); return; }

Type guard

function audioContextSupported(): boolean {
    return typeof AudioContext !== 'undefined' || typeof (window as any).webkitAudioContext !== 'undefined';
}

Try / catch

try { recorder = new RecordMedia(stream); }
catch (e) { if (/AudioContext/i.test(e.message)) showMessage('Voice recording unsupported in this browser'); }

Prevention

When it happens

Trigger: Constructing RecordMedia in a browser/webview that does not implement Web Audio (very old or stripped-down webview), in an insecure context where AudioContext is gated, or in a test environment (jsdom) without a Web Audio polyfill.

Common situations: Old Android System WebView; an iframe served over http (not localhost) where AudioContext is blocked; headless test runner; an enterprise policy disabling Web Audio.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/8f9decceb9010b7e. Report an issue: GitHub.