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
- Read error.errorMsg — when it is 'Unknown error', the server gave no detail and you must debug the input
- Re-submit the same input once: transient processing failures often succeed on retry
- If it fails deterministically, validate the document locally (open it, check encryption, try a smaller page range) and re-export or compress it
- 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
- Validate documents locally (opens, not encrypted, sane size) before submission
- Log error.jobId and error.errorMsg so server-side failures are traceable
- Retry opaque failures once; treat descriptive errorMsg as a permanent input problem
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
- Timed out after ${timeoutMs}ms waiting for job ${jobId}
- Unknown job state: ${data.state}
- Status progress must be an object.
- Done job response is missing resultUrl.jsonUrl.
- Job {job_id} failed: {error_msg}
AI-assisted analysis of PaddlePaddle/PaddleOCR@2661c7c0ef (2026-08-14).
Data as JSON: /api/errors/bc01c7bd0357eb92.
Report an issue: GitHub.