{"record":{"id":"6dfd1d9f69376943","repo":"siyuan-note/siyuan","slug":"mp3-encoder-is-not-initialized","errorCode":null,"errorMessage":"MP3 encoder is not initialized","messagePattern":"MP3 encoder is not initialized","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"app/src/protyle/util/RecordMediaWorker.ts","lineNumber":33,"sourceCode":"let encoder: Mp3Encoder;\n\nconst postChunk = (chunk: Uint8Array) => {\n    if (chunk.length === 0) {\n        return;\n    }\n    const buffer = chunk.slice().buffer;\n    workerScope.postMessage({type: \"chunk\", buffer}, [buffer]);\n};\n\nworkerScope.onmessage = (event: MessageEvent<WorkerMessage>) => {\n    try {\n        const message = event.data;\n        if (message.type === \"init\") {\n            encoder = new Mp3Encoder(1, message.sampleRate, message.bitRate);\n            workerScope.postMessage({type: \"ready\"});\n        } else if (message.type === \"encode\") {\n            if (!encoder) {\n                throw new Error(\"MP3 encoder is not initialized\");\n            }\n            postChunk(encoder.encodeBuffer(new Int16Array(message.buffer)));\n        } else if (message.type === \"finish\") {\n            if (!encoder) {\n                throw new Error(\"MP3 encoder is not initialized\");\n            }\n            postChunk(encoder.flush());\n            encoder = undefined;\n            workerScope.postMessage({type: \"finished\"});\n        }\n    } catch (error) {\n        workerScope.postMessage({\n            type: \"error\",\n            message: error instanceof Error ? error.message : String(error),\n        });\n    }\n};\n","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/app/src/protyle/util/RecordMediaWorker.ts#L15-L51","documentation":"In RecordMediaWorker.ts the 'encode' branch throws 'MP3 encoder is not initialized' when encoder is undefined — i.e. when a chunk arrives before the 'init' message created the Mp3Encoder, or after a 'finish' message set encoder = undefined. The error is posted back to the main thread as {type:'error', message}.","triggerScenarios":"AudioWorklet posts 'chunk' messages (forwarded as 'encode') before the worker finished processing 'init'; 'finish' runs (encoder reset to undefined) and then a late 'encode' arrives from a still-running AudioWorkletNode; the 'init' message was lost or never sent.","commonSituations":"Recorder started before worker.onmessage was wired (init never reached the worker); stopRecording() then a stray chunk from the AudioWorklet; worker restarted mid-session losing the encoder variable; misordered message queue.","solutions":["Ensure 'init' is the first message posted to the worker and is awaited (readyPromise) before posting 'encode'.","After 'finish', also close the AudioWorkletNode / disconnect the audio path so no late 'encode' can arrive.","Make the worker ignore (not throw on) 'encode'/'finish' when encoder is undefined, posting a 'finished' or 'error' as appropriate.","Add a worker-state guard so encodeBuffer is only called when state==='ready'."],"exampleFix":"// before\nelse if (message.type === 'encode') {\n    if (!encoder) { throw new Error('MP3 encoder is not initialized'); }\n    postChunk(encoder.encodeBuffer(new Int16Array(message.buffer)));\n}\n// after\nelse if (message.type === 'encode') {\n    if (!encoder) { workerScope.postMessage({type:'error', message:'encode before init/after finish — ignored'}); return; }\n    postChunk(encoder.encodeBuffer(new Int16Array(message.buffer)));\n}","handlingStrategy":"validation","validationCode":"if (!encoderReady) { /* drop or queue encode until readyPromise resolves */ }","typeGuard":null,"tryCatchPattern":"this.worker.onmessage = (e) => {\n    if (e.data.type === 'error' && /not initialized/i.test(e.data.message)) { /* swallow or flag */ return; }\n};","preventionTips":["Always post 'init' first and await readyPromise before posting 'encode'.","After 'finish', disconnect the AudioWorkletNode so no late encode arrives.","Make the worker no-op (rather than throw) when encode arrives without an encoder.","Track a worker state machine (init -> ready -> encoding -> finished) and reject out-of-state messages."],"tags":["web-audio","recording","mp3","web-worker","lifecycle","typescript"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}