siyuan-note/siyuan · error

AudioContext is not supported

Error message

AudioContext is not supported

What it means

The RecordMedia constructor needs an AudioContext (or webkitAudioContext) to analyze the microphone stream. If neither global exists in the runtime, it throws 'AudioContext is not supported'. The environment cannot provide the Web Audio API required for recording.

Solutions

  1. Serve the app over HTTPS (or localhost) so the Web Audio API is available
  2. Update the platform's webview/browser to a version supporting AudioContext
  3. Catch this error in the recording UI and show 'recording not supported in this environment' instead of crashing
  4. Feature-detect typeof AudioContext === "undefined" before attempting to record and hide the record button

Example fix

// before
const recorder = new RecordMedia(stream);
// after
if (typeof AudioContext === "undefined" && typeof webkitAudioContext === "undefined") {
  showMessage(window.siyuan.languages.audioNotSupported);
  return;
}
const recorder = new RecordMedia(stream);
Defensive patterns

Strategy: fallback

Validate before calling

const audioSupported =
  typeof AudioContext !== "undefined" || typeof webkitAudioContext !== "undefined";

Type guard

const hasAudioContext = (): boolean =>
  typeof (window as any).AudioContext !== "undefined" ||
  typeof (window as any).webkitAudioContext !== "undefined";

Try / catch

let recorder: RecordMedia | null = null;
try {
  recorder = new RecordMedia(stream);
} catch (e) {
  if (e.message === "AudioContext is not supported") {
    showMessage("Recording is not supported in this environment");
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Instantiating RecordMedia with a MediaStream in an environment where both AudioContext and webkitAudioContext are undefined — e.g. a non-secure (http) page where Web Audio is unavailable, an old webview, or a very old browser.

Common situations: Recording feature used inside an outdated embedded webview; page served over plain HTTP so some audio APIs are restricted; very old Android System WebView; SSR/test environment lacking browser globals.

Understand the failure class

Background: "unsupported platform" / "not supported on this platform" errors: what they mean and how to fix them — this error's family across 47 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/8f9decceb9010b7e. Report an issue: GitHub.

Appendix: 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 9f775e8a12)