naptha/tesseract.js · error · Error

[${id}]: You need to have at least one worker before adding

Error message

[${id}]: You need to have at least one worker before adding jobs

What it means

A scheduler routes queued jobs to its pool of workers via dequeue(). addJob guards with getNumWorkers() (Object.keys(workers).length) because a job pushed with no workers would never dispatch. The throw is synchronous inside the async addJob, so it rejects the returned promise immediately.

Source

Thrown at src/createScheduler.js:63

        }
      });
      log(`[${id}]: Add ${job.id} to JobQueue`);
      log(`[${id}]: JobQueue length=${jobQueue.length}`);
      dequeue();
    })
  );

  const addWorker = (w) => {
    workers[w.id] = w;
    log(`[${id}]: Add ${w.id}`);
    log(`[${id}]: Number of workers=${getNumWorkers()}`);
    dequeue();
    return w.id;
  };

  const addJob = async (action, ...payload) => {
    if (getNumWorkers() === 0) {
      throw Error(`[${id}]: You need to have at least one worker before adding jobs`);
    }
    return queue(action, payload);
  };

  const terminate = async () => {
    Object.keys(workers).forEach(async (wid) => {
      await workers[wid].terminate();
    });
    jobQueue = [];
  };

  return {
    addWorker,
    addJob,
    terminate,
    getQueueLen,
    getNumWorkers,
  };

View on GitHub (pinned to a1ca80d9e3)

Solutions

  1. Await createWorker(...) to resolve before calling scheduler.addWorker(worker) and only then call scheduler.addJob(...).
  2. Guard with scheduler.getNumWorkers() > 0 before addJob, and spawn a worker if zero.
  3. Ensure workers are added once at startup (e.g. a pool of N workers) rather than per-request.

Example fix

// before
const scheduler = createScheduler();
createWorker('eng').then((w) => scheduler.addWorker(w)); // not awaited
scheduler.addJob('recognize', img); // throws: worker not added yet

// after
const scheduler = createScheduler();
const worker = await createWorker('eng');
scheduler.addWorker(worker);
await scheduler.addJob('recognize', img);
Defensive patterns

Strategy: validation

Validate before calling

if (scheduler.getNumWorkers() === 0) {
  const w = await createWorker('eng');
  scheduler.addWorker(w);
}
await scheduler.addJob('recognize', image);

Try / catch

try {
  await scheduler.addJob('recognize', image);
} catch (e) {
  if (/at least one worker/i.test(e.message)) {
    scheduler.addWorker(await createWorker('eng'));
    await scheduler.addJob('recognize', image);
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling scheduler.addJob('recognize', image) before any scheduler.addWorker(worker). Most commonly a race: createWorker(...) is async and the caller invokes addJob before the worker promise resolves and is passed to addWorker.

Common situations: Forgetting to spawn workers; awaiting createWorker in parallel with addJob; refactoring that removes the addWorker step; calling addJob on a fresh scheduler created by createScheduler().


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