{"record":{"id":"079b3d6000e6e735","repo":"moeru-ai/airi","slug":"inferenceworkermanager-restarting-worker-in-de","errorCode":null,"errorMessage":"[InferenceWorkerManager] Restarting worker in ${delay}ms (attempt ${restartAttempts}/${maxRestarts})","messagePattern":"\\[InferenceWorkerManager\\] Restarting worker in (.+?)ms \\(attempt (.+?)/(.+?)\\)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/stage-ui/src/libs/inference/worker-manager.ts","lineNumber":207,"sourceCode":"  function destroyWorker(): void {\n    if (worker) {\n      worker.terminate()\n      worker = null\n    }\n  }\n\n  function scheduleRestart(): void {\n    if (restartAttempts >= maxRestarts) {\n      console.error(\n        `[InferenceWorkerManager] Max restart attempts (${maxRestarts}) reached. Giving up.`,\n      )\n      return\n    }\n\n    restartAttempts++\n    const delay = restartDelayMs * restartAttempts\n\n    console.warn(\n      `[InferenceWorkerManager] Restarting worker in ${delay}ms `\n      + `(attempt ${restartAttempts}/${maxRestarts})`,\n    )\n\n    setTimeout(() => {\n      ensureStarted().catch((err) => {\n        console.error('[InferenceWorkerManager] Failed to restart:', errorMessageFrom(err))\n      })\n    }, delay)\n  }\n\n  function onSuccessfulOperation(): void {\n    restartAttempts = 0\n  }\n\n  async function ensureStarted(): Promise<void> {\n    await lifecycleMutex.runExclusive(async () => {\n      if (!worker) {","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/stage-ui/src/libs/inference/worker-manager.ts#L189-L225","documentation":"Scheduled-restart notice from InferenceWorkerManager, the generic lifecycle wrapper for inference Web Workers in packages/stage-ui/src/libs/inference/worker-manager.ts. When the managed worker dies or fails terminally, scheduleRestart() re-spawns it via ensureStarted() with linear backoff (restartDelayMs multiplied by attempt number; defaults 1000ms base, 3 max attempts from WorkerManagerOptions). This line is only the retry announcement - the actual crash cause appears earlier in the console or on manager.lastError.","triggerScenarios":"The worker exits unexpectedly (uncaught exception or out-of-memory inside the worker during transformers.js model load or inference), a loadModel/run request fails hard enough to trigger the restart path, or the browser tears the worker down. Each failure increments restartAttempts and logs 'Restarting worker in <delay>ms (attempt N/M)'. After maxRestarts failures the manager logs 'Max restart attempts reached. Giving up.' and stops.","commonSituations":"WebGPU device lost inside the worker, OOM when loading a large model, stale worker bundle after dev HMR, or a deterministic crash loop (bad model file, unsupported device, missing import) that burns all 3 attempts. Note the file header: no adapter currently consumes this manager, so seeing it means custom code built on it.","solutions":["Scroll up in the console for the originating worker error, or read manager.lastError - this log only reports the retry, not the cause","If the log sequence ends with 'Max restart attempts reached', reproduce the worker failure directly and fix the root cause (model id, quantization, device support, import failure)","Raise maxRestarts or restartDelayMs in the options passed to createInferenceWorkerManager when the crash is transient, e.g. a GPU reset during startup","Reduce model size or quantization, or force device 'wasm', if the worker dies from OOM during load","After the manager gives up (state 'error'), call terminate() and build a fresh manager instead of expecting automatic recovery"],"exampleFix":"// before\nconst manager = createInferenceWorkerManager({ createWorker })\n// crash loop exhausts defaults: 3 attempts, 1s base delay\n\n// after\nconst manager = createInferenceWorkerManager({\n  createWorker,\n  maxRestarts: 5,\n  restartDelayMs: 2_000, // backoff: 2s, 4s, 6s...\n})","handlingStrategy":"retry","validationCode":"if (manager.state !== 'ready' && manager.state !== 'running') {\n  await manager.loadModel({ modelId, device: 'wasm' })\n}","typeGuard":"function isUsableManager(m: InferenceWorkerManager): boolean {\n  return m.state === 'ready' || m.state === 'running'\n}","tryCatchPattern":"try {\n  await manager.run(input)\n}\ncatch (err) {\n  if (manager.state === 'error' || manager.state === 'terminated') {\n    manager.terminate()\n    // rebuild the manager, then retry the operation once\n  }\n  else throw err\n}","preventionTips":["Watch manager.state and manager.lastError programmatically instead of parsing console output","Size models to the worker's memory budget so restarts are rare","Treat repeated restart warnings as a crash loop - fix the underlying worker error rather than raising maxRestarts","Test with the same device (webgpu/wasm) as production browsers"],"tags":["web-worker","restart","backoff","inference","lifecycle"],"backgroundTag":"worker-crash-restart-backoff","analyzedSha":"677329427f32468c74b17f3ec47eeca4e05bec65","analyzedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-08-23T16:17:53.355Z"}