{"record":{"id":"28c25edf8f4a7f11","repo":"PaddlePaddle/PaddleOCR","slug":"worker-transport-client-has-been-disposed","errorCode":null,"errorMessage":"Worker transport client has been disposed.","messagePattern":"Worker transport client has been disposed\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"paddleocr-js/packages/core/src/worker/client.ts","lineNumber":34,"sourceCode":"\nexport class WorkerTransportClient {\n  private workerOptions: WorkerOptions;\n  private worker: Worker | null;\n  private pending: Map<number, PendingRequest>;\n  private nextRequestId: number;\n  private disposed: boolean;\n\n  constructor(workerOptions: WorkerOptions = {}) {\n    this.workerOptions = workerOptions;\n    this.worker = null;\n    this.pending = new Map();\n    this.nextRequestId = 1;\n    this.disposed = false;\n  }\n\n  ensureActive(): void {\n    if (this.disposed) {\n      throw new Error(\"Worker transport client has been disposed.\");\n    }\n  }\n\n  ensureWorker(): Worker {\n    this.ensureActive();\n    if (this.worker) {\n      return this.worker;\n    }\n\n    const workerFactory = this.workerOptions.createWorker;\n    if (typeof workerFactory !== \"function\") {\n      throw new Error(\"Worker transport client requires a createWorker() factory.\");\n    }\n    const worker = workerFactory();\n    worker.onmessage = (event: MessageEvent) => {\n      const message = event.data as unknown;\n      if (!isTransportResponse(message)) return;\n      const pending = this.pending.get(message.requestId);","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/paddleocr-js/packages/core/src/worker/client.ts#L16-L52","documentation":"Thrown by WorkerTransportClient.ensureActive() when any operation is attempted after dispose() was called. The client sets disposed = true on teardown (worker terminated, pending rejected); every subsequent recognize/rpc call re-checks the flag and fails fast rather than silently operating on a dead transport. This is a lifecycle-contract error: the instance is single-use after disposal.","triggerScenarios":"Awaiting client.dispose() (or an error path that disposes) and then issuing another request; shared client held in app state that gets disposed on unmount but referenced later by a lingering promise/timer; React StrictMode double-mount disposing a client another effect still uses.","commonSituations":"Component unmount racing an in-flight recognition that retries; hot module replacement disposing the old client while callbacks still hold it; caching the client in a module-level variable without resetting it after dispose.","solutions":["Create a fresh WorkerTransportClient (or re-create the OCR instance) after disposing","Null the reference on dispose and guard call sites with the disposed check","Cancel/await pending work before calling dispose in teardown logic"],"exampleFix":"// before\nclient.dispose();\nawait client.recognize(img); // throws: disposed\n\n// after\nclient.dispose();\nclient = createWorkerTransport(options); // new client for further work\nawait client.recognize(img);","handlingStrategy":"type-guard","validationCode":"// call before every use\nclient.ensureActive(); // throws early with a clear message","typeGuard":"function isActiveClient(c: { ensureActive(): void }): boolean {\n  try { c.ensureActive(); return true; } catch { return false; }\n}","tryCatchPattern":"try {\n  return await client.recognize(img);\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"disposed\")) {\n    client = createClient(); // rebuild transport and retry once\n    return await client.recognize(img);\n  }\n  throw e;\n}","preventionTips":["Null out references in dispose() teardown paths","Await or cancel in-flight work before disposing","Re-create the client after dispose instead of reusing it"],"tags":["worker","lifecycle","state-management"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}