{"record":{"id":"bc01c7bd0357eb92","repo":"PaddlePaddle/PaddleOCR","slug":"job-jobid-failed-errormsg","errorCode":null,"errorMessage":"Job ${jobId} failed: ${errorMsg}","messagePattern":"Job (.+?) failed: (.+?)","errorType":"exception","errorClass":"JobFailedError","httpStatus":null,"severity":"error","filePath":"api_sdk/typescript/src/internal/poller.ts","lineNumber":52,"sourceCode":"  async pollUntilDone(jobId: string, signal?: AbortSignal): Promise<unknown[]> {\n    let interval = INITIAL_INTERVAL;\n    const deadline = Date.now() + this.maxWaitTime;\n\n    while (Date.now() < deadline) {\n      throwIfAborted(signal);\n\n      const remaining = deadline - Date.now();\n      const data = await this.withPollTimeout(jobId, remaining, () => this.http.getJobStatus(jobId, signal, remaining));\n      const status = normalizeStatus(jobId, data);\n\n      if (status.state === \"done\") {\n        const jsonUrl = resultJsonUrl(data);\n        const resultRemaining = deadline - Date.now();\n        return await this.withPollTimeout(jobId, resultRemaining, () => this.http.fetchJsonl(jsonUrl, signal, resultRemaining));\n      }\n\n      if (status.state === \"failed\") {\n        throw new JobFailedError(jobId, status.errorMsg || \"Unknown error\");\n      }\n\n      await this.sleep(Math.min(interval, Math.max(0, deadline - Date.now())), signal);\n      interval = Math.min(interval * MULTIPLIER, MAX_INTERVAL);\n    }\n\n    throw new PollTimeoutError(jobId, this.maxWaitTime);\n  }\n\n  async getStatus(jobId: string, signal?: AbortSignal): Promise<JobStatus> {\n    return normalizeStatus(jobId, await this.http.getJobStatus(jobId, signal));\n  }\n\n  async getBatchStatus(batchId: string, signal?: AbortSignal): Promise<BatchStatus> {\n    const data = await this.http.getBatchStatus(batchId, signal);\n    if (!isRecord(data) || !Array.isArray(data.extractResult)) {\n      throw new ResponseFormatError(\"Batch response is missing extractResult.\");\n    }","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/api_sdk/typescript/src/internal/poller.ts#L34-L70","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"try {\n  const result = await poller.waitForResult(jobId);\n} catch (e) {\n  if (e instanceof JobFailedError) {\n    logger.error({ jobId: e.jobId, reason: e.errorMsg }, \"job failed\");\n    if (e.errorMsg === \"Unknown error\") {\n      return poller.waitForResult(await client.submitFile(model, prunedFile, {}));\n    }\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"import fs from \"node:fs\";\nfunction looksProcessable(file: string): boolean {\n  // cheap local checks: exists, non-empty, not obviously encrypted\n  const buf = fs.readFileSync(file).subarray(0, 1024);\n  return buf.length > 0 && !file.toLowerCase().endsWith(\".pdf.enc\");\n}","typeGuard":"function isJobFailedError(e: unknown): e is JobFailedError {\n  return e instanceof JobFailedError;\n}","tryCatchPattern":"try {\n  const result = await poller.waitForResult(jobId);\n} catch (e) {\n  if (e instanceof JobFailedError) {\n    if (e.errorMsg === \"Unknown error\") return resubmitOnce(e.jobId); // opaque: retry once\n    throw new Error(`Document rejected (${e.errorMsg}) — fix input`);  // descriptive: permanent\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["job","terminal-state","polling","typescript"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}