PaddlePaddle/PaddleOCR · error · Error

Worker transport client has been disposed.

Error message

Worker transport client has been disposed.

What it means

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.

Source

Thrown at paddleocr-js/packages/core/src/worker/client.ts:34

export class WorkerTransportClient {
  private workerOptions: WorkerOptions;
  private worker: Worker | null;
  private pending: Map<number, PendingRequest>;
  private nextRequestId: number;
  private disposed: boolean;

  constructor(workerOptions: WorkerOptions = {}) {
    this.workerOptions = workerOptions;
    this.worker = null;
    this.pending = new Map();
    this.nextRequestId = 1;
    this.disposed = false;
  }

  ensureActive(): void {
    if (this.disposed) {
      throw new Error("Worker transport client has been disposed.");
    }
  }

  ensureWorker(): Worker {
    this.ensureActive();
    if (this.worker) {
      return this.worker;
    }

    const workerFactory = this.workerOptions.createWorker;
    if (typeof workerFactory !== "function") {
      throw new Error("Worker transport client requires a createWorker() factory.");
    }
    const worker = workerFactory();
    worker.onmessage = (event: MessageEvent) => {
      const message = event.data as unknown;
      if (!isTransportResponse(message)) return;
      const pending = this.pending.get(message.requestId);

View on GitHub (pinned to 2661c7c0ef)

Solutions

  1. Create a fresh WorkerTransportClient (or re-create the OCR instance) after disposing
  2. Null the reference on dispose and guard call sites with the disposed check
  3. Cancel/await pending work before calling dispose in teardown logic

Example fix

// before
client.dispose();
await client.recognize(img); // throws: disposed

// after
client.dispose();
client = createWorkerTransport(options); // new client for further work
await client.recognize(img);
Defensive patterns

Strategy: type-guard

Validate before calling

// call before every use
client.ensureActive(); // throws early with a clear message

Type guard

function isActiveClient(c: { ensureActive(): void }): boolean {
  try { c.ensureActive(); return true; } catch { return false; }
}

Try / catch

try {
  return await client.recognize(img);
} catch (e) {
  if (e instanceof Error && e.message.includes("disposed")) {
    client = createClient(); // rebuild transport and retry once
    return await client.recognize(img);
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of PaddlePaddle/PaddleOCR@2661c7c0ef (2026-08-14). Data as JSON: /api/errors/28c25edf8f4a7f11. Report an issue: GitHub.