{"record":{"id":"4372db251887a5e6","repo":"PaddlePaddle/PaddleOCR","slug":"paddleocr-worker-instance-has-been-disposed","errorCode":null,"errorMessage":"PaddleOCR worker instance has been disposed.","messagePattern":"PaddleOCR worker instance has been disposed\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"paddleocr-js/packages/core/src/pipelines/ocr/worker-backed.ts","lineNumber":43,"sourceCode":"  private options: OcrPipelineRunnerOptions;\n  private lastInitializationSummary: InitializationSummary | null;\n  private modelConfig: OcrModelConfig;\n  private transportClient: WorkerTransportClient;\n  private initPromise: Promise<InitializationSummary> | null;\n  private disposed: boolean;\n\n  constructor(options: OcrPipelineRunnerOptions, transportClient: WorkerTransportClient) {\n    this.options = options;\n    this.lastInitializationSummary = null;\n    this.modelConfig = cloneDefaultOcrConfig();\n    this.transportClient = transportClient;\n    this.initPromise = null;\n    this.disposed = false;\n  }\n\n  ensureActive(): void {\n    if (this.disposed) {\n      throw new Error(\"PaddleOCR worker instance has been disposed.\");\n    }\n  }\n\n  async initialize(): Promise<InitializationSummary> {\n    this.ensureActive();\n    if (this.lastInitializationSummary) {\n      return this.lastInitializationSummary;\n    }\n    if (!this.initPromise) {\n      const ortOpts = (this.options.ortOptions || {}) as Record<string, unknown>;\n      if (ortOpts[\"wasmPaths\"] === undefined && typeof __ORT_WASM_CDN_PREFIX__ === \"string\") {\n        console.warn(\n          \"[PaddleOCR.js] Worker mode: ortOptions.wasmPaths is not set — falling back to CDN (%s). \" +\n            \"For version consistency between main thread and worker, set ortOptions.wasmPaths \" +\n            \"to the path where your bundler outputs the onnxruntime-web WASM files \" +\n            '(e.g. ortOptions: { wasmPaths: \"/assets/\" }).',\n          __ORT_WASM_CDN_PREFIX__\n        );","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/paddleocr-js/packages/core/src/pipelines/ocr/worker-backed.ts#L25-L61","documentation":"WorkerBackedPaddleOCR tracks a disposed flag set by dispose(). Every public method calls ensureActive() first and throws this error after disposal, preventing use of a terminated worker whose transport client no longer has a live counterpart.","triggerScenarios":"Calling predict(), initialize(), setRuntimeParams(), etc. on a WorkerBackedPaddleOCR instance after await instance.dispose(); also double-dispose followed by reuse, or a race where a queued call resolves after dispose().","commonSituations":"Cleaning up on component unmount / route change while an in-flight prediction continues; shared singleton disposed by one consumer while another still uses it; React StrictMode double effects disposing then reusing.","solutions":["Create a fresh instance via PaddleOCR.create() after disposing instead of reusing the old one.","Serialize teardown: await outstanding predict() promises before calling dispose().","For shared instances, reference-count users and dispose only when the count reaches zero."],"exampleFix":"// before\nawait ocr.dispose();\nconst result = await ocr.predict(image); // throws\n\n// after\nawait ocr.dispose();\nconst ocr2 = await PaddleOCR.create();\nconst result = await ocr2.predict(image);","handlingStrategy":"validation","validationCode":"// Track your own lifecycle: never call predict() after dispose()\nlet disposed = false;\nasync function teardown() {\n  disposed = true;\n  await inFlight; // settle outstanding predictions\n  await ocr.dispose();\n}\nif (!disposed) await ocr.predict(img);","typeGuard":null,"tryCatchPattern":"try {\n  await ocr.predict(image);\n} catch (e) {\n  if (e instanceof Error && e.message === 'PaddleOCR worker instance has been disposed.') {\n    ocr = await PaddleOCR.create(opts); // recreate and optionally retry once\n    return ocr.predict(image);\n  }\n  throw e;\n}","preventionTips":["Await all in-flight predictions before dispose(); keep a promise counter if calls are frequent.","Reference-count shared instances across components before tearing down.","Do not reuse an instance after dispose() — always create a new one."],"tags":["lifecycle","worker","dispose","usage-error"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}