{"record":{"id":"30e6854e174b7f23","repo":"PaddlePaddle/PaddleOCR","slug":"unsafe-resource-filename-key","errorCode":null,"errorMessage":"Unsafe resource filename: ${key}","messagePattern":"Unsafe resource filename: (.+?)","errorType":"exception","errorClass":"InvalidRequestError","httpStatus":null,"severity":"warning","filePath":"api_sdk/typescript/src/client.ts","lineNumber":374,"sourceCode":"  for (const [key, val] of Object.entries(value)) {\n    if (typeof val === \"string\") {\n      result[key] = val;\n    }\n  }\n  return result;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n  return typeof value === \"object\" && value !== null;\n}\n\nfunction isDocParsingResult(result: OCRResult | DocParsingResult): result is DocParsingResult {\n  return result.pages.some((page) => \"markdownText\" in page);\n}\n\nfunction safeMapKeyFilename(key: string): string {\n  if (!key || key === \".\" || key === \"..\" || key.includes(\"/\") || key.includes(\"\\\\\") || key.startsWith(\".\")) {\n    throw new InvalidRequestError(`Unsafe resource filename: ${key}`);\n  }\n  return key;\n}\n\nfunction safeUrlBasename(url: URL): string {\n  const name = basename(url.pathname) || \"resource\";\n  if (name === \".\" || name === \"..\") {\n    return \"resource\";\n  }\n  return name;\n}\n\nfunction resourceExtension(resourceUrl: string): string {\n  try {\n    const url = new URL(resourceUrl);\n    return extname(url.pathname);\n  } catch {\n    return \"\";","sourceCodeStart":356,"sourceCodeEnd":392,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/api_sdk/typescript/src/client.ts#L356-L392","documentation":"Raised as NetworkError from fetch_jsonl in paddleocr/_api_client/_http.py:192 when the connection to the pre-signed resultUrl fails (DNS, TLS, reset, refused). The result file lives on object storage, a different host from the API — so this can fail even when API connectivity is fine (e.g. storage domain blocked). The job succeeded; only the artifact download failed and can be retried while the pre-signed URL is valid.","triggerScenarios":"client.get_result() where the object-storage host of resultUrl.jsonUrl is unreachable: firewall blocks the storage domain, DNS fails for the CDN bucket, or TLS interception breaks the storage cert.","commonSituations":"Corporate firewalls whitelisting the API domain but not object storage/CDN domains; egress-restricted containers; regional storage outages; expired pre-signed URLs surfacing as connection-level errors via middleboxes.","solutions":["Extract jsonUrl from job status and test it directly (curl) from the same environment","Whitelist the object-storage/CDN host in firewall/proxy rules","Retry promptly — pre-signed URLs expire; if expired, re-fetch job status for a fresh URL","Check the storage provider's status page for outages"],"exampleFix":"# verify the result URL is reachable\nstatus = client.get_job_status(job_id)\nurl = status[\"resultUrl\"][\"jsonUrl\"]\nimport requests\nr = requests.get(url, timeout=30)\nprint(r.status_code, len(r.content))","handlingStrategy":"retry","validationCode":"# preflight: is the result storage host reachable?\nstatus = client.get_job_status(job_id)\nurl = status.get(\"resultUrl\", {}).get(\"jsonUrl\")\nif url:\n    host = urlparse(url).hostname\n    try:\n        socket.getaddrinfo(host, 443)\n    except socket.gaierror:\n        print(f\"Result storage host {host} not resolvable — check firewall/DNS\")","typeGuard":null,"tryCatchPattern":"from paddleocr._api_client.errors import NetworkError\ntry:\n    results = client.get_result(job_id)\nexcept NetworkError:\n    time.sleep(3)\n    results = client.get_result(job_id)  # pre-signed URL still valid; retry download","preventionTips":["Whitelist the object-storage/CDN host in firewalls, not just the API domain","Retry downloads promptly — pre-signed URLs expire","Extract and curl the jsonUrl when diagnosing environment-specific blocks"],"tags":["network","connection","download","result","presigned-url","retryable"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}