{"record":{"id":"102bbb0dd0befbb1","repo":"PaddlePaddle/PaddleOCR","slug":"resource-url-is-required","errorCode":null,"errorMessage":"resource_url is required.","messagePattern":"resource_url is required\\.","errorType":"exception","errorClass":"InvalidRequestError","httpStatus":null,"severity":"warning","filePath":"paddleocr/_api_client/_resources.py","lineNumber":36,"sourceCode":"from typing import Dict, Iterable, List, Optional, Tuple\nfrom urllib.parse import unquote, urlparse\n\nimport requests\n\nfrom .errors import InvalidRequestError, NetworkError, RequestTimeoutError\nfrom .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:","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/paddleocr/_api_client/_resources.py#L18-L54","documentation":"InvalidRequestError raised by save_resource when resource_url is falsy (empty string or None). It is a client-side argument guard run before any URL parsing or network I/O — nothing has been requested yet when it fires.","triggerScenarios":"Calling save_resource('', dest) or save_resource(None, dest) directly; or higher-level helpers passing through an empty URL field from a result payload (e.g. a page whose ocr_image_url is empty — though save_ocr_result_resources skips those, direct calls do not).","commonSituations":"Programmatically extracting URLs from a result object where some fields are legitimately empty and forgetting the empty check; refactoring that renames fields and silently yields None; calling save_resource with a variable that was never assigned on an error path.","solutions":["Check the caller: log the value being passed and find why it is empty","Guard before calling: skip empty URLs or raise a domain-specific error with context (which page/field was empty)","If the URL should never be empty, validate upstream data (the result payload) for missing fields"],"exampleFix":"# before\nsave_resource(page.ocr_image_url, dest)  # crashes when URL is ''\n\n# after\nif page.ocr_image_url:\n    save_resource(page.ocr_image_url, dest)","handlingStrategy":"validation","validationCode":"if not resource_url:\n    raise ValueError(f\"resource_url is empty (field={field_name!r})\")","typeGuard":"def is_http_url(u) -> bool:\n    return isinstance(u, str) and u.startswith(('http://', 'https://')) and len(u) > 8","tryCatchPattern":"from paddleocr._api_client.errors import InvalidRequestError\ntry:\n    save_resource(url, dest)\nexcept InvalidRequestError as e:\n    logger.warning(\"skipping resource: %s\", e)  # treat empty URLs as non-fatal","preventionTips":["Filter empty URL fields out of result objects before iterating","Name the missing field in your own guard so the error is traceable to a page/index"],"tags":["validation","arguments","download"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}