Error Fix: The Web Worker Payload Misnomer That Broke Batch Extractions
TLDR
After building a Web Worker pool for off-thread multi-document ingestion, batch file uploads reportedCOMPLETED status but failed to render any extracted content when focused. The main thread WorkerPool dispatched message payloads with key buffer, whereas the Web Worker listener destructured bytes. Because bytes evaluated to undefined, TextDecoder and document parsers received invalid buffers and returned empty HTML strings ("").
| Layer | Declared Property Key | Evaluated Value | Extraction Result |
|---|---|---|---|
workerPool.js (Line 53) | buffer: arrayBuffer | Valid ArrayBuffer | Sent across worker boundary |
pipelineWorker.js (Line 34) | const { bytes } = e.data | undefined | Fails: Cannot decode undefined |
Fixed pipelineWorker.js | buffer ? new Uint8Array(buffer) : null | Valid Uint8Array | Success: Valid HTML extracted |
Technical defect analysis
When batch items were processed by WorkerPool, postMessage transferred an ArrayBuffer under the property name buffer:
// workerPool.js (Main Thread)
workerEntry.worker.postMessage({
type: 'process_batch_item',
id: task.item.id,
format: task.item.format,
buffer: arrayBuffer // Property name is 'buffer'
}, [arrayBuffer]);
Inside the background worker (pipelineWorker.js), the event handler attempted to destructure bytes:
// DEFECTIVE: pipelineWorker.js
self.onmessage = async (e) => {
if (e.data.type === 'process_batch_item') {
const { id, bytes, format } = e.data; // Fails: e.data.bytes is undefined!
let extractedHTML = ''; if (format === 'html') { // TextDecoder(undefined) returns empty string without throwing! extractedHTML = new TextDecoder().decode(bytes); }
self.postMessage({ type: 'batch_item_complete', id, result: { extractedHTML } }); } };
Because TextDecoder and PDF parsers did not throw fatal exceptions on undefined or null inputs, execution reached self.postMessage successfully. The batch item status was updated to completed, but contained an empty string ("") for extractedHTML.
Remediation: universal payload extraction
Update pipelineWorker.js to extract bytes from either rawBytes or buffer:
// REFACTORED: pipelineWorker.js
self.onmessage = async (e) => {
if (e.data.type === 'process_batch_item') {
const { id, buffer, bytes: rawBytes, fileName, format } = e.data;
// Coerce both buffer and rawBytes into a valid Uint8Array
const bytes = rawBytes ? new Uint8Array(rawBytes) : (buffer ? new Uint8Array(buffer) : null);
try { let extractedHTML = ''; let extractedText = ''; let gxDoc = null;
if (bytes && (format === 'html' || format === 'md')) { const text = new TextDecoder().decode(bytes); extractedText = text; extractedHTML = format === 'md' ? <pre>${text}</pre> : text; }
self.postMessage({ type: 'batch_item_complete', id, result: { gxDoc, extractedHTML, extractedText, bytes: bytes ? bytes.buffer : null } }, bytes ? [bytes.buffer] : []); } catch (err) { self.postMessage({ type: 'batch_item_error', id, error: err.message }); } } };
Why this fix works
- Fallback Coercion: Handlers gracefully convert
ArrayBufferinputs (buffer) or byte arrays (rawBytes) into a validUint8Array. - Transferable Return: The worker transfers
bytes.bufferback to the main thread, allowing PDF canvas renderers to draw cached page data instantly.
Rule of thumb: When passing objects across Web Worker boundaries, handle property key aliases explicitly to prevent silent undefined serialization errors.