{"record":{"id":"4d31bed3f195a573","repo":"PaddlePaddle/PaddleOCR","slug":"unknown-job-state-data-state","errorCode":null,"errorMessage":"Unknown job state: ${data.state}","messagePattern":"Unknown job state: (.+?)","errorType":"exception","errorClass":"ResponseFormatError","httpStatus":null,"severity":"error","filePath":"api_sdk/typescript/src/internal/poller.ts","lineNumber":117,"sourceCode":"      throw new PollTimeoutError(jobId, this.maxWaitTime);\n    }\n    try {\n      return await operation();\n    } catch (error) {\n      if (error instanceof RequestTimeoutError && error.timeoutMs === remainingMs) {\n        throw new PollTimeoutError(jobId, this.maxWaitTime, { cause: error });\n      }\n      throw error;\n    }\n  }\n}\n\nfunction normalizeStatus(jobId: string, data: unknown): JobStatus {\n  if (!isRecord(data) || typeof data.state !== \"string\") {\n    throw new ResponseFormatError(\"Status response is missing state.\");\n  }\n  if (![\"pending\", \"running\", \"done\", \"failed\"].includes(data.state)) {\n    throw new ResponseFormatError(`Unknown job state: ${data.state}`);\n  }\n  return {\n    jobId,\n    state: data.state as JobStatus[\"state\"],\n    progress: normalizeProgress(data.extractProgress),\n    resultUrl: isRecord(data.resultUrl) ? stringMap(data.resultUrl) : undefined,\n    errorMsg: typeof data.errorMsg === \"string\" ? data.errorMsg : undefined,\n  };\n}\n\nfunction normalizeProgress(progress: unknown): Progress | undefined {\n  if (progress === undefined || progress === null) {\n    return undefined;\n  }\n  if (!isRecord(progress)) {\n    throw new ResponseFormatError(\"Status progress must be an object.\");\n  }\n  return {","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/api_sdk/typescript/src/internal/poller.ts#L99-L135","documentation":"Thrown by normalizeStatus() while polling a job: the status response carried a state field, but its value is not one of the four states this SDK understands (\"pending\", \"running\", \"done\", \"failed\"). It is a ResponseFormatError, meaning the server reply violated the contract the SDK's poller was built against. This almost always indicates the server and SDK disagree about the job-state enum.","triggerScenarios":"Calling the poll/wait API (e.g. waitForResult or whatever drives normalizeStatus) against a server that returns a newer state such as \"cancelled\", \"canceling\", \"expired\", \"queued\", or a typo'd value like \"Done\". Also produced by mock/stub servers that invent state names.","commonSituations":"Server API upgraded to add a cancel/timeout state while the client pins an older SDK; test harness returning hardcoded JSON with a wrong state string; regional deployments with divergent API versions.","solutions":["Log the raw HTTP status response body to see the exact state string the server sent","Upgrade the TypeScript SDK to a version that knows the new state value (check changelog for job state additions)","If you control the server, restrict emitted states to pending/running/done/failed until clients are updated","If the new state is terminal-cancel, add server-side mapping to an SDK-known state or expose it via errorMsg on state=\"failed\""],"exampleFix":"// before\nconst result = await client.waitForResult(jobId); // throws on unknown state\n\n// after\nconst job = await client.getJob(jobId); // raw status call that tolerates unknown states\nif ([\"pending\",\"running\"].includes(job.state)) { /* keep polling via waitForResult */ }\nelse if (job.state === \"done\") { /* fetch result */ }\nelse { /* handle terminal/unknown without the strict poller */ }","handlingStrategy":"type-guard","validationCode":"const KNOWN_STATES = new Set([\"pending\", \"running\", \"done\", \"failed\"]);\nconst status = await client.getJobStatus(jobId); // raw status call\nif (!KNOWN_STATES.has(status.state)) {\n  console.warn(`Server reported unknown state ${status.state}; SDK poller would throw`);\n}","typeGuard":"function isKnownJobState(s: string): s is \"pending\" | \"running\" | \"done\" | \"failed\" {\n  return [\"pending\", \"running\", \"done\", \"failed\"].includes(s);\n}","tryCatchPattern":"try {\n  const result = await poller.wait(jobId);\n} catch (e) {\n  if (e instanceof ResponseFormatError && /Unknown job state/.test(e.message)) {\n    // version skew with server: fetch raw status and decide manually\n  } else throw e;\n}","preventionTips":["Pin SDK and server to compatible API versions; check the changelog when the server adds job states","Monitor raw status payloads in staging so new state values surface before they break the poller","Never invent state strings in mock servers; use the four documented states"],"tags":["api-sdk","typescript","polling","version-skew","contract-mismatch"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}