{"record":{"id":"c01d19612c7ed229","repo":"PaddlePaddle/PaddleOCR","slug":"destination-must-be-an-existing-directory-desti","errorCode":null,"errorMessage":"Destination must be an existing directory: ${destination}","messagePattern":"Destination must be an existing directory: (.+?)","errorType":"exception","errorClass":"InvalidRequestError","httpStatus":null,"severity":"error","filePath":"api_sdk/typescript/src/client.ts","lineNumber":194,"sourceCode":"\n  private collectMappedResourcePlans(resources: Record<string, string>): ResourceSavePlan[] {\n    return Object.keys(resources)\n      .sort()\n      .map((key) => ({\n        resourceUrl: resources[key],\n        filename: safeMapKeyFilename(key),\n      }));\n  }\n\n  private async requireExistingDirectory(destination: string): Promise<void> {\n    let destinationStat;\n    try {\n      destinationStat = await stat(destination);\n    } catch {\n      throw new FileNotFoundError(destination);\n    }\n    if (!destinationStat.isDirectory()) {\n      throw new InvalidRequestError(`Destination must be an existing directory: ${destination}`);\n    }\n  }\n\n  private async requireWritableTarget(target: string, options: SaveResourceOptions): Promise<void> {\n    try {\n      await stat(target);\n    } catch {\n      return;\n    }\n    if (!options.overwrite) {\n      throw new InvalidRequestError(`Destination already exists: ${target}`);\n    }\n  }\n\n  private requireUniqueTargets(targets: string[], options: SaveResourceOptions): void {\n    if (options.overwrite) {\n      return;\n    }","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/api_sdk/typescript/src/client.ts#L176-L212","documentation":"Thrown by parse_batch_status in paddleocr/_api_client/_core.py:204 while iterating data.extractResult: an element of that list is not a JSON object. Each element must be an object describing one job (jobId, state, ...); a scalar/string/null element means the server returned a malformed batch listing. ResponseFormatError, raised during batch status parsing.","triggerScenarios":"get_batch_status where extractResult is a list but contains non-dict entries, e.g. [\"job-1\", \"job-2\"] instead of [{\"jobId\": \"job-1\", ...}].","commonSituations":"Server bug or schema drift; a proxied/cached response from a different API version; hand-rolled mock server returning simplified batch payloads.","solutions":["Inspect the raw extractResult array contents","If testing against a mock, make each element an object with at least a non-empty string jobId and a valid state","Upgrade the SDK / check API changelog for batch response changes","Report to PaddleOCR with the raw payload if the production server is emitting non-object entries"],"exampleFix":"# mock fix\n# before\n{\"extractResult\": [\"job-1\", \"job-2\"]}\n# after\n{\"extractResult\": [{\"jobId\": \"job-1\", \"state\": \"done\"}, {\"jobId\": \"job-2\", \"state\": \"pending\"}]}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"def valid_batch_items(data: dict) -> bool:\n    items = data.get(\"extractResult\")\n    return isinstance(items, list) and all(isinstance(i, dict) for i in items)","tryCatchPattern":"from paddleocr._api_client.errors import ResponseFormatError\ntry:\n    batch = client.get_batch_status(batch_id)\nexcept ResponseFormatError:\n    logger.exception(\"Batch listing malformed; dumping raw response\")\n    raise","preventionTips":["Make test doubles return object items, never bare id strings","Contract-test the batch endpoint shape in CI"],"tags":["api","response-format","batch","schema"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}