siyuan-note/siyuan · error · Error
MP3 encoder is not initialized
Error message
MP3 encoder is not initialized
What it means
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}.
Source
Thrown at app/src/protyle/util/RecordMediaWorker.ts:33
let encoder: Mp3Encoder;
const postChunk = (chunk: Uint8Array) => {
if (chunk.length === 0) {
return;
}
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 251596fc0d)
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'.
Example fix
// before
else if (message.type === 'encode') {
if (!encoder) { throw new Error('MP3 encoder is not initialized'); }
postChunk(encoder.encodeBuffer(new Int16Array(message.buffer)));
}
// after
else if (message.type === 'encode') {
if (!encoder) { workerScope.postMessage({type:'error', message:'encode before init/after finish — ignored'}); return; }
postChunk(encoder.encodeBuffer(new Int16Array(message.buffer)));
} Defensive patterns
Strategy: validation
Validate before calling
if (!encoderReady) { /* drop or queue encode until readyPromise resolves */ } Try / catch
this.worker.onmessage = (e) => {
if (e.data.type === 'error' && /not initialized/i.test(e.data.message)) { /* swallow or flag */ return; }
}; Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Recorder has been disposed
- AudioContext is not supported
- AudioWorklet is not supported
- Failed to save agent session
- Failed to remove agent session
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/6dfd1d9f69376943.
Report an issue: GitHub.