{"record":{"id":"8f9decceb9010b7e","repo":"siyuan-note/siyuan","slug":"audiocontext-is-not-supported","errorCode":null,"errorMessage":"AudioContext is not supported","messagePattern":"AudioContext is not supported","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"app/src/protyle/util/RecordMedia.ts","lineNumber":95,"sourceCode":"    private worker: Worker;\n    private chunks: ArrayBuffer[] = [];\n    private readyPromise: Promise<void>;\n    private resolveReady: () => void;\n    private rejectReady: (error: Error) => void;\n    private stopPromise: Promise<Blob>;\n    private resolveStop: (blob: Blob) => void;\n    private rejectStop: (error: Error) => void;\n    private disposed = false;\n    private failure: Error;\n    private readonly handleTrackEnded = () => {\n        this.handleWorkerError(new RecordMediaInputEndedError());\n    };\n\n    constructor(mediaStream: MediaStream) {\n        this.mediaStream = mediaStream;\n        const AudioContextConstructor = typeof AudioContext !== \"undefined\" ? AudioContext : webkitAudioContext;\n        if (!AudioContextConstructor) {\n            throw new Error(\"AudioContext is not supported\");\n        }\n\n        const contextOptions = {} as AudioContextOptions & { sinkId?: { type: \"none\" } };\n        if ((isInAndroid() || isInHarmony()) && \"setSinkId\" in AudioContextConstructor.prototype) {\n            contextOptions.sinkId = {type: \"none\"};\n        }\n        this.context = new AudioContextConstructor(contextOptions);\n        if (!this.context.audioWorklet || typeof AudioWorkletNode === \"undefined\") {\n            throw new Error(\"AudioWorklet is not supported\");\n        }\n        this.audioInput = this.context.createMediaStreamSource(mediaStream);\n        this.mediaStream.getAudioTracks().forEach((track) => {\n            track.addEventListener(\"ended\", this.handleTrackEnded);\n        });\n    }\n\n    public async startRecording() {\n        if (this.disposed) {","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/app/src/protyle/util/RecordMedia.ts#L77-L113","documentation":"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.","triggerScenarios":"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.","commonSituations":"Old Android System WebView; an iframe served over http (not localhost) where AudioContext is blocked; headless test runner; an enterprise policy disabling Web Audio.","solutions":["Feature-detect AudioContext/webkitAudioContext before instantiating RecordMedia and disable the record button if missing.","Ensure the page is served in a secure context (https or localhost) so the browser does not gate Web Audio.","Update the host browser/webview to a version that ships Web Audio.","In tests, polyfill AudioContext (standardized-audio-context mock)."],"exampleFix":"// before\nconst rec = new RecordMedia(stream);\n// after\nconst Ctor = typeof AudioContext !== 'undefined' ? AudioContext : (window as any).webkitAudioContext;\nif (!Ctor) { showMessage('Audio recording is not supported in this browser'); return; }\nconst rec = new RecordMedia(stream);","handlingStrategy":"type-guard","validationCode":"const Ctor = typeof AudioContext !== 'undefined' ? AudioContext : (window as any).webkitAudioContext;\nif (!Ctor) { disableRecordButton(); return; }","typeGuard":"function audioContextSupported(): boolean {\n    return typeof AudioContext !== 'undefined' || typeof (window as any).webkitAudioContext !== 'undefined';\n}","tryCatchPattern":"try { recorder = new RecordMedia(stream); }\ncatch (e) { if (/AudioContext/i.test(e.message)) showMessage('Voice recording unsupported in this browser'); }","preventionTips":["Feature-detect AudioContext before showing the record button.","Serve the app over https or localhost (insecure contexts block Web Audio).","Keep the host browser/webview up to date.","Polyfill Web Audio in tests."],"tags":["web-audio","recording","feature-detection","typescript"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}