PaddlePaddle/PaddleOCR · error · JobFailedError

Job ${jobId} failed: ${errorMsg}

Error message

Job ${jobId} failed: ${errorMsg}

What it means

JobFailedError means the job transitioned to the 'failed' state server-side while the poller was waiting for it. The message embeds the jobId and the server-provided errorMsg ('Unknown error' when the status payload carried none); both are also exposed as properties (error.jobId, error.errorMsg). This is a terminal state — retrying the wait will not help; a new job must be submitted.

Source

Thrown at api_sdk/typescript/src/internal/poller.ts:52

  async pollUntilDone(jobId: string, signal?: AbortSignal): Promise<unknown[]> {
    let interval = INITIAL_INTERVAL;
    const deadline = Date.now() + this.maxWaitTime;

    while (Date.now() < deadline) {
      throwIfAborted(signal);

      const remaining = deadline - Date.now();
      const data = await this.withPollTimeout(jobId, remaining, () => this.http.getJobStatus(jobId, signal, remaining));
      const status = normalizeStatus(jobId, data);

      if (status.state === "done") {
        const jsonUrl = resultJsonUrl(data);
        const resultRemaining = deadline - Date.now();
        return await this.withPollTimeout(jobId, resultRemaining, () => this.http.fetchJsonl(jsonUrl, signal, resultRemaining));
      }

      if (status.state === "failed") {
        throw new JobFailedError(jobId, status.errorMsg || "Unknown error");
      }

      await this.sleep(Math.min(interval, Math.max(0, deadline - Date.now())), signal);
      interval = Math.min(interval * MULTIPLIER, MAX_INTERVAL);
    }

    throw new PollTimeoutError(jobId, this.maxWaitTime);
  }

  async getStatus(jobId: string, signal?: AbortSignal): Promise<JobStatus> {
    return normalizeStatus(jobId, await this.http.getJobStatus(jobId, signal));
  }

  async getBatchStatus(batchId: string, signal?: AbortSignal): Promise<BatchStatus> {
    const data = await this.http.getBatchStatus(batchId, signal);
    if (!isRecord(data) || !Array.isArray(data.extractResult)) {
      throw new ResponseFormatError("Batch response is missing extractResult.");
    }

View on GitHub (pinned to 2661c7c0ef)

Solutions

  1. Read error.errorMsg — when it is 'Unknown error', the server gave no detail and you must debug the input
  2. Re-submit the same input once: transient processing failures often succeed on retry
  3. If it fails deterministically, validate the document locally (open it, check encryption, try a smaller page range) and re-export or compress it
  4. Report jobs that fail with 'Unknown error' using error.jobId so support can trace server logs

Example fix

try {
  const result = await poller.waitForResult(jobId);
} catch (e) {
  if (e instanceof JobFailedError) {
    logger.error({ jobId: e.jobId, reason: e.errorMsg }, "job failed");
    if (e.errorMsg === "Unknown error") {
      return poller.waitForResult(await client.submitFile(model, prunedFile, {}));
    }
  }
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

import fs from "node:fs";
function looksProcessable(file: string): boolean {
  // cheap local checks: exists, non-empty, not obviously encrypted
  const buf = fs.readFileSync(file).subarray(0, 1024);
  return buf.length > 0 && !file.toLowerCase().endsWith(".pdf.enc");
}

Type guard

function isJobFailedError(e: unknown): e is JobFailedError {
  return e instanceof JobFailedError;
}

Try / catch

try {
  const result = await poller.waitForResult(jobId);
} catch (e) {
  if (e instanceof JobFailedError) {
    if (e.errorMsg === "Unknown error") return resubmitOnce(e.jobId); // opaque: retry once
    throw new Error(`Document rejected (${e.errorMsg}) — fix input`);  // descriptive: permanent
  }
  throw e;
}

Prevention

When it happens

Trigger: waitForResult()/poll loops on a job the backend could not process: corrupt or unreadable uploaded document, unsupported content inside the file (e.g. encrypted PDF), server-side OCR pipeline crash, or resource limits killing the job after acceptance.

Common situations: Password-protected or badly-formed PDFs; scanned images in unsupported color spaces; huge documents exceeding server memory; rare backend incidents that mark jobs failed without a descriptive errorMsg.

Related errors


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