{"record":{"id":"816d6669a632190e","repo":"PaddlePaddle/PaddleOCR","slug":"invalid-resource-url-resource-url","errorCode":null,"errorMessage":"Invalid resource URL: {resource_url}","messagePattern":"Invalid resource URL: (.+?)","errorType":"exception","errorClass":"InvalidRequestError","httpStatus":null,"severity":"warning","filePath":"paddleocr/_api_client/_resources.py","lineNumber":42,"sourceCode":"from .results import DocParsingResult, OCRResult\n\n\ndef save_resource(\n    resource_url: str,\n    destination: str,\n    *,\n    overwrite: bool = False,\n    filename: Optional[str] = None,\n    timeout: float = 300.0,\n) -> str:\n    if not resource_url:\n        raise InvalidRequestError(\"resource_url is required.\")\n    if not destination:\n        raise InvalidRequestError(\"destination is required.\")\n\n    parsed_url = urlparse(resource_url)\n    if parsed_url.scheme not in (\"http\", \"https\") or not parsed_url.netloc:\n        raise InvalidRequestError(f\"Invalid resource URL: {resource_url}\")\n\n    target = _resolve_destination(parsed_url.path, destination, filename)\n    _require_writable_target(target, overwrite)\n\n    try:\n        response = requests.get(resource_url, timeout=timeout)\n    except requests.Timeout as e:\n        raise RequestTimeoutError(f\"Request timed out: {e}\") from e\n    except requests.ConnectionError as e:\n        raise NetworkError(f\"Connection failed: {e}\") from e\n\n    try:\n        response.raise_for_status()\n    except requests.RequestException as e:\n        raise NetworkError(f\"Failed to download resource: {e}\") from e\n\n    _atomic_write(target, response.content, overwrite)\n    return str(target)","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/paddleocr/_api_client/_resources.py#L24-L60","documentation":"InvalidRequestError raised when resource_url does not parse as an absolute http(s) URL: the scheme is not http/https or the netloc is empty. It accepts only well-formed absolute web URLs; local paths and other schemes are rejected before any request is made.","triggerScenarios":"Passing '/tmp/result.json', 'file:///data/img.png', 'ftp://host/f', or a bare 'host/path' (no scheme) to save_resource. Also a URL that got mangled by string formatting (e.g. an f-string that dropped the scheme or left 'None' in place of the host).","commonSituations":"Result payloads where an image URL is actually a relative path or object-store key rather than a full URL; manually constructed URLs; copy-pasting a path instead of a URL; using an s3:// or gs:// URL where only http(s) is supported.","solutions":["Fix the URL: prepend 'https://' when the server returns scheme-less hosts, or resolve relative paths against the API base URL","If you need local/other-scheme sources, download/copy them yourself — save_resource intentionally supports only http(s)","Log the offending URL at the call site to catch malformed values early"],"exampleFix":"# before\nsave_resource(f\"{host}{path}\", dest)  # host missing scheme -> rejected\n\n# after\nif not resource_url.startswith(('http://', 'https://')):\n    resource_url = f\"https://{resource_url.lstrip('/')}\"\nsave_resource(resource_url, dest)","handlingStrategy":"validation","validationCode":"from urllib.parse import urlparse\np = urlparse(resource_url)\nassert p.scheme in ('http', 'https') and p.netloc, f'bad resource URL: {resource_url!r}'","typeGuard":"def is_absolute_http_url(u: str) -> bool:\n    p = urlparse(u)\n    return p.scheme in ('http', 'https') and bool(p.netloc)","tryCatchPattern":"try:\n    save_resource(url, dest)\nexcept InvalidRequestError as e:\n    if 'Invalid resource URL' in str(e):\n        url = f'https://{url}'  # repair scheme-less host and retry once\n        save_resource(url, dest)\n    else:\n        raise","preventionTips":["Normalize server-returned keys/hosts to absolute https URLs at ingestion time","Reserve save_resource for web URLs; handle local/other-scheme sources separately"],"tags":["validation","url","download"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}