PaddlePaddle/PaddleOCR · error · ResponseFormatError

Done job response is missing resultUrl.jsonUrl.

Error message

Done job response is missing resultUrl.jsonUrl.

What it means

Thrown by resultJsonUrl() when a job reaches the "done" state but its response does not contain a resultUrl object with a string jsonUrl. The poller treats a done job as incomplete without a result location, so it refuses rather than returning a result it cannot fetch. This is a ResponseFormatError pointing at a server-side contract violation or an incomplete job payload.

Source

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

function normalizeProgress(progress: unknown): Progress | undefined {
  if (progress === undefined || progress === null) {
    return undefined;
  }
  if (!isRecord(progress)) {
    throw new ResponseFormatError("Status progress must be an object.");
  }
  return {
    totalPages: typeof progress.totalPages === "number" ? progress.totalPages : 0,
    extractedPages: typeof progress.extractedPages === "number" ? progress.extractedPages : 0,
    startTime: typeof progress.startTime === "string" ? progress.startTime : undefined,
    endTime: typeof progress.endTime === "string" ? progress.endTime : undefined,
  };
}

function resultJsonUrl(data: unknown): string {
  if (!isRecord(data) || !isRecord(data.resultUrl) || typeof data.resultUrl.jsonUrl !== "string") {
    throw new ResponseFormatError("Done job response is missing resultUrl.jsonUrl.");
  }
  return data.resultUrl.jsonUrl;
}

function stringMap(value: Record<string, unknown>): Record<string, string> {
  const result: Record<string, string> = {};
  for (const [key, val] of Object.entries(value)) {
    if (typeof val === "string") {
      result[key] = val;
    }
  }
  return result;
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null;
}

View on GitHub (pinned to 2661c7c0ef)

Solutions

  1. Log the full done-state response body to see what resultUrl actually contains
  2. If resultUrl is a plain string, the server is on an older API shape — align SDK and server versions
  3. If the field is intermittently missing, fix the server to publish the result URL atomically with the done transition
  4. As a caller, retry the status fetch once after a short delay if you control the server and suspect the publish race

Example fix

// before (server response)
{ "state": "done", "resultUrl": "https://cdn/.../result.json" }

// after
{ "state": "done", "resultUrl": { "jsonUrl": "https://cdn/.../result.json" } }
Defensive patterns

Strategy: validation

Validate before calling

const job = await client.getJob(jobId);
if (job.state === "done" && !(typeof job.resultUrl?.jsonUrl === "string")) {
  // done without a result location: retry once after a delay, then report server bug
}

Type guard

function hasJsonUrl(job: unknown): job is { resultUrl: { jsonUrl: string } } {
  const j = job as Record<string, any>;
  return typeof j?.resultUrl?.jsonUrl === "string";
}

Try / catch

try { await poller.wait(jobId); } catch (e) {
  if (e instanceof ResponseFormatError && /resultUrl\.jsonUrl/.test(e.message)) {
    // likely a publish race on the server: re-fetch status once after ~1s before giving up
  } else throw e;
}

Prevention

When it happens

Trigger: Poller observes state="done" and immediately tries to read data.resultUrl.jsonUrl, but the server returned done without resultUrl, with resultUrl as a string URL instead of an object, or with jsonUrl missing/non-string. Also occurs when jobs are marked done before the result artifact URL is written.

Common situations: Race on the server between status flip to done and result upload; server renames resultUrl.jsonUrl (e.g. to resultUrl.json); resultUrl serialized as a plain string in an older API version.

Related errors


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