{"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":null,"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/9f775e8a12daef8255556097396f9b2739078892/app/src/protyle/util/RecordMedia.ts#L77-L113","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Serve the app over HTTPS (or localhost) so the Web Audio API is available","Update the platform's webview/browser to a version supporting AudioContext","Catch this error in the recording UI and show 'recording not supported in this environment' instead of crashing","Feature-detect typeof AudioContext === \"undefined\" before attempting to record and hide the record button"],"exampleFix":"// before\nconst recorder = new RecordMedia(stream);\n// after\nif (typeof AudioContext === \"undefined\" && typeof webkitAudioContext === \"undefined\") {\n  showMessage(window.siyuan.languages.audioNotSupported);\n  return;\n}\nconst recorder = new RecordMedia(stream);","handlingStrategy":"fallback","validationCode":"const audioSupported =\n  typeof AudioContext !== \"undefined\" || typeof webkitAudioContext !== \"undefined\";","typeGuard":"const hasAudioContext = (): boolean =>\n  typeof (window as any).AudioContext !== \"undefined\" ||\n  typeof (window as any).webkitAudioContext !== \"undefined\";","tryCatchPattern":"let recorder: RecordMedia | null = null;\ntry {\n  recorder = new RecordMedia(stream);\n} catch (e) {\n  if (e.message === \"AudioContext is not supported\") {\n    showMessage(\"Recording is not supported in this environment\");\n    return;\n  }\n  throw e;\n}","preventionTips":["Serve the app over HTTPS or localhost","Feature-detect AudioContext before showing the record button","Keep embedded webviews updated","Gate recording behind a capability check at app startup"],"tags":["audio","recording","browser-support"],"backgroundTag":"unsupported-platform","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}