PaddlePaddle/PaddleOCR · error · InvalidRequestError

Invalid resource URL: ${resourceUrl}

Error message

Invalid resource URL: ${resourceUrl}

What it means

Thrown by validate_result_json_url in paddleocr/_api_client/_core.py:189 when the done job's resultUrl object exists but its 'jsonUrl' field is not a non-empty string. jsonUrl is the pre-signed URL the SDK downloads JSONL results from; an empty/missing/null value makes download impossible. This is a ResponseFormatError indicating a malformed server response for a completed job.

Source

Thrown at api_sdk/typescript/src/client.ts:131

  }

  async saveDocumentParsingResultResources(
    result: DocParsingResult,
    destination: string,
    options: SaveResourceOptions = {},
  ): Promise<string[]> {
    return this.saveResultResources(result, destination, options);
  }

  private async saveResourceUrl(resourceUrl: string, destination: string, options: SaveResourceOptions): Promise<string> {
    if (!resourceUrl) {
      throw new InvalidRequestError("resourceUrl is required.");
    }
    let url: URL;
    try {
      url = new URL(resourceUrl);
    } catch (error) {
      throw new InvalidRequestError(`Invalid resource URL: ${resourceUrl}`, { cause: error });
    }
    const target = await this.resolveDestination(url, destination, options);
    const content = await this.http.fetchResource(resourceUrl);
    await writeFile(target, Buffer.from(content), { flag: options.overwrite ? "w" : "wx" });
    return target;
  }

  private async saveResultResources(
    result: OCRResult | DocParsingResult,
    destination: string,
    options: SaveResourceOptions,
  ): Promise<string[]> {
    await this.requireExistingDirectory(destination);
    const plans = this.collectResultResourcePlans(result);
    const targets = plans.map((plan) => join(destination, plan.filename));
    for (const target of targets) {
      await this.requireWritableTarget(target, options);
    }

View on GitHub (pinned to 2661c7c0ef)

Solutions

  1. Wait a few seconds and poll job status again — jsonUrl may appear once artifact generation settles
  2. Dump the raw status response to confirm resultUrl contents
  3. If jsonUrl is consistently absent for done jobs, upgrade the SDK and/or report to PaddleOCR — the server response is malformed
  4. As a workaround, check whether resultUrl carries an alternative field (e.g. a zip URL) and fetch results manually
Defensive patterns

Strategy: try-catch

Validate before calling

status = client.get_job_status(job_id)
ru = status.get("resultUrl")
if isinstance(ru, dict) and not (isinstance(ru.get("jsonUrl"), str) and ru["jsonUrl"]:
    print("jsonUrl not ready")

Type guard

def has_json_url(data: dict) -> bool:
    ru = data.get("resultUrl")
    return isinstance(ru, dict) and isinstance(ru.get("jsonUrl"), str) and bool(ru["jsonUrl"])

Try / catch

from paddleocr._api_client.errors import ResponseFormatError
try:
    result = client.get_result(job_id)
except ResponseFormatError as e:
    if "jsonUrl" in str(e):
        time.sleep(5)
        result = client.get_result(job_id)

Prevention

When it happens

Trigger: Retrieving results of a done job where data.resultUrl exists but resultUrl.jsonUrl is absent, empty, null, or a non-string (e.g. a list of URLs).

Common situations: Result artifacts not yet generated when the job flipped to done; pre-signed URL generation failing server-side; schema drift in the API; truncated response from a proxy.

Related errors


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