naptha/tesseract.js · critical · Error

${data}

Error message

${data}

What it means

When the worker thread rejects a job (status === 'reject'), onMessage rejects the corresponding promise and then either invokes the user-supplied errorHandler or, if none was provided, runs throw Error(data) inside the message-event listener. Because that listener is not an awaited async function, the throw surfaces as an uncaught exception rather than a normal rejection, and the thrown message is just the stringified worker-side error data.

Source

Thrown at src/createWorker.js:217

    return Promise.resolve();
  };

  onMessage(worker, ({
    workerId, jobId, status, action, data,
  }) => {
    const promiseId = `${action}-${jobId}`;
    if (status === 'resolve') {
      log(`[${workerId}]: Complete ${jobId}`);
      promises[promiseId].resolve({ jobId, data });
      delete promises[promiseId];
    } else if (status === 'reject') {
      promises[promiseId].reject(data);
      delete promises[promiseId];
      if (action === 'load') workerResReject(data);
      if (errorHandler) {
        errorHandler(data);
      } else {
        throw Error(data);
      }
    } else if (status === 'progress') {
      logger({ ...data, userJobId: jobId });
    }
  });

  const resolveObj = {
    id,
    worker,
    load,
    writeText,
    readText,
    removeFile,
    FS,
    reinitialize,
    setParameters,
    recognize,
    detect,

View on GitHub (pinned to a1ca80d9e3)

Solutions

  1. Pass errorHandler: (err) => { /* log/handle */ } in the options to createWorker — this replaces the throw.
  2. Ensure every awaited worker job has a .catch() so the promise rejection path is handled.
  3. As a last line of defense, register a process-level uncaughtException listener in Node.js.

Example fix

// before
const worker = await createWorker('eng'); // no errorHandler
await worker.recognize(badImg); // worker rejects -> uncaught throw

// after
const worker = await createWorker('eng', {
  errorHandler: (err) => console.error('worker error:', err),
});
try { await worker.recognize(badImg); } catch (e) { /* handled */ }
Defensive patterns

Strategy: try-catch

Try / catch

// Always pass an errorHandler so the internal `throw Error(data)` branch is never reached.
const worker = await createWorker('eng', OEM.LSTM_ONLY, {
  errorHandler: (err) => {
    // log/transport; do NOT rethrow inside the message listener
    logger.error('tesseract worker error:', err);
  },
});

// AND always await jobs inside try/catch:
try {
  const { data } = await worker.recognize(image);
} catch (jobErr) {
  // the promise-rejection path; safe to handle/rethrow here
  logger.error('recognize failed:', jobErr);
}

Prevention

When it happens

Trigger: Any worker-side job rejection (image decode failure, recognize crash, FS error) when createWorker was called without an errorHandler option. The same rejection is delivered twice: once to the awaited promise, once as the uncaught throw.

Common situations: Default config in Node.js where no errorHandler is set; image/load failures during processing crashing the process; confusing double-reporting of the same error.


AI-assisted analysis of naptha/tesseract.js@a1ca80d9e3 (2026-08-13). Data as JSON: /api/errors/f530444680836f19. Report an issue: GitHub.