{"record":{"id":"dd2ce95091e3a180","repo":"naptha/tesseract.js","slug":"id-you-need-to-have-at-least-one-worker-befo","errorCode":null,"errorMessage":"[${id}]: You need to have at least one worker before adding jobs","messagePattern":"\\[(.+?)\\]: You need to have at least one worker before adding jobs","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/createScheduler.js","lineNumber":63,"sourceCode":"        }\n      });\n      log(`[${id}]: Add ${job.id} to JobQueue`);\n      log(`[${id}]: JobQueue length=${jobQueue.length}`);\n      dequeue();\n    })\n  );\n\n  const addWorker = (w) => {\n    workers[w.id] = w;\n    log(`[${id}]: Add ${w.id}`);\n    log(`[${id}]: Number of workers=${getNumWorkers()}`);\n    dequeue();\n    return w.id;\n  };\n\n  const addJob = async (action, ...payload) => {\n    if (getNumWorkers() === 0) {\n      throw Error(`[${id}]: You need to have at least one worker before adding jobs`);\n    }\n    return queue(action, payload);\n  };\n\n  const terminate = async () => {\n    Object.keys(workers).forEach(async (wid) => {\n      await workers[wid].terminate();\n    });\n    jobQueue = [];\n  };\n\n  return {\n    addWorker,\n    addJob,\n    terminate,\n    getQueueLen,\n    getNumWorkers,\n  };","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/naptha/tesseract.js/blob/a1ca80d9e31c34512d0ded75ff8821ddcf3f2f91/src/createScheduler.js#L45-L81","documentation":"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.","triggerScenarios":"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.","commonSituations":"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().","solutions":["Await createWorker(...) to resolve before calling scheduler.addWorker(worker) and only then call scheduler.addJob(...).","Guard with scheduler.getNumWorkers() > 0 before addJob, and spawn a worker if zero.","Ensure workers are added once at startup (e.g. a pool of N workers) rather than per-request."],"exampleFix":"// before\nconst scheduler = createScheduler();\ncreateWorker('eng').then((w) => scheduler.addWorker(w)); // not awaited\nscheduler.addJob('recognize', img); // throws: worker not added yet\n\n// after\nconst scheduler = createScheduler();\nconst worker = await createWorker('eng');\nscheduler.addWorker(worker);\nawait scheduler.addJob('recognize', img);","handlingStrategy":"validation","validationCode":"if (scheduler.getNumWorkers() === 0) {\n  const w = await createWorker('eng');\n  scheduler.addWorker(w);\n}\nawait scheduler.addJob('recognize', image);","typeGuard":null,"tryCatchPattern":"try {\n  await scheduler.addJob('recognize', image);\n} catch (e) {\n  if (/at least one worker/i.test(e.message)) {\n    scheduler.addWorker(await createWorker('eng'));\n    await scheduler.addJob('recognize', image);\n  } else { throw e; }\n}","preventionTips":["Always await createWorker and call addWorker in the same synchronous block before any addJob.","Build the worker pool once at application startup and reuse the scheduler.","Wrap addJob in a helper that lazily spawns a worker when getNumWorkers() === 0."],"tags":["scheduler","api-misuse","async","worker-pool"],"backgroundTag":null,"analyzedSha":"a1ca80d9e31c34512d0ded75ff8821ddcf3f2f91","analyzedAt":"2026-08-13T04:28:13.744Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}