siyuan-note/siyuan · error · Error
MP3 encoder is not initialized
Error message
MP3 encoder is not initialized
What it means
Inside the RecordMedia worker script, messages of type "encode" or "finish" require the MP3 encoder instance to exist. If encoder is undefined (worker not initialized, or an encode/finish arrives after finish already reset it), it throws 'MP3 encoder is not initialized'.
Solutions
- Ensure the worker receives a valid init message (with correct sample rate/channels) before any encode/finish message
- Track recording state in the UI and disable encode/finish posts after finish has been sent
- Guard the main thread: only post finish once, and await worker ready before sending audio chunks
- In the worker, log message.type and encoder state in the catch handler to identify the out-of-order message
Example fix
// before (main thread)
worker.postMessage({type: "finish"});
worker.postMessage({type: "finish"}); // second call throws
// after
let finished = false;
const finish = () => {
if (finished) return;
finished = true;
worker.postMessage({type: "finish"});
}; Defensive patterns
Strategy: try-catch
Validate before calling
let encoderReady = false;
worker.addEventListener("message", e => { if (e.data.type === "ready") encoderReady = true; });
// before encode/finish:
if (!encoderReady) throw new Error("worker not initialized"); Type guard
const canPostToRecorderWorker = (state: {initialized: boolean, finished: boolean}): boolean =>
state.initialized && !state.finished; Try / catch
worker.onerror = e => {
if (String(e.message).includes("MP3 encoder is not initialized")) {
console.error("Recording worker used out of order; re-initializing");
initWorker(worker);
}
}; Prevention
- Always send init before any encode/finish message
- Send finish exactly once per recording session
- Reset initialized state after finish and re-init before the next recording
- Handle worker error events to detect out-of-order messages early
When it happens
Trigger: Posting {type: "encode"} or {type: "finish"} to the recording worker without a prior successful init message, posting encode after finish reset encoder to undefined, or the init message failed silently.
Common situations: Recording stop (finish) races with a final queued encode message; worker reused across recordings without re-init; init payload (sample rate/channels) failed causing encoder creation to be skipped.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- attribute view is not unused
- AudioContext is not supported
- block swap notebook has changed
- cannot change master password while encrypted notebooks are…
- cannot disable encrypted notebook feature while encrypted…
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/6dfd1d9f69376943.
Report an issue: GitHub.
Appendix: source
Thrown at app/src/protyle/util/RecordMediaWorker.ts:38
}
const buffer = chunk.slice().buffer;
workerScope.postMessage({type: "chunk", buffer}, [buffer]);
};
workerScope.onmessage = (event: MessageEvent<WorkerMessage>) => {
try {
const message = event.data;
if (message.type === "init") {
encoder = new Mp3Encoder(1, message.sampleRate, message.bitRate);
workerScope.postMessage({type: "ready"});
} else if (message.type === "encode") {
if (!encoder) {
throw new Error("MP3 encoder is not initialized");
}
postChunk(encoder.encodeBuffer(new Int16Array(message.buffer)));
} else if (message.type === "finish") {
if (!encoder) {
throw new Error("MP3 encoder is not initialized");
}
postChunk(encoder.flush());
encoder = undefined;
workerScope.postMessage({type: "finished"});
}
} catch (error) {
workerScope.postMessage({
type: "error",
message: error instanceof Error ? error.message : String(error),
});
}
};
View on GitHub (pinned to 9f775e8a12)