{"record":{"id":"cf59f8ebc47c6143","repo":"xtekky/gpt4free","slug":"url-must-be-a-non-empty-string","errorCode":null,"errorMessage":"url must be a non-empty string","messagePattern":"url must be a non-empty string","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"g4f/integration/markitdown/__init__.py","lineNumber":237,"sourceCode":"        )\n        if m:\n            owner, repo, ref, path = (m.group(1), m.group(2), m.group(3), m.group(4))\n            # Strip a trailing slash if any\n            path = path.rstrip(\"/\")\n            return f\"https://raw.githubusercontent.com/{owner}/{repo}/{ref}/{path}\"\n\n        # Tree (directory) URLs and repo roots: cannot map to a single raw file\n        return url\n\n    def convert_url(\n        self,\n        url: str,\n        *,\n        stream_info: Optional[StreamInfo] = None,\n        **kwargs: Any,\n    ) -> DocumentConverterResult:\n        if url is None or not isinstance(url, str) or url.strip() == \"\":\n            raise ValueError(\"url must be a non-empty string\")\n        if not url.startswith((\"http://\", \"https://\")):\n            raise ValueError(\"url must start with http:// or https://\")\n        if url.startswith(\"https://github.com/\"):\n            # Special case for GitHub URLs -- convert to raw content URL\n            url = self._convert_github_url_to_raw(url)\n        return super().convert_url(url, stream_info=stream_info, **kwargs)\n","sourceCodeStart":219,"sourceCodeEnd":244,"githubUrl":"https://github.com/xtekky/gpt4free/blob/973504e1770928ed5fb82f43da528f441ad9ddc3/g4f/integration/markitdown/__init__.py#L219-L244","documentation":"Thrown by MarkItDown.convert_url() when url is None, not a str, or a whitespace-only string. It is the input-contract check run before the scheme check; anything that is not a non-empty string is rejected without any network attempt.","triggerScenarios":"convert_url(None); convert_url(123); convert_url('   '); convert_url(['https://a']) (a list/tuple instead of a single string).","commonSituations":"Passing a value read from JSON/config where the key was missing (None); passing a urllib.parse result object instead of .geturl(); forgetting to unpack a list of URLs before looping.","solutions":["Validate before calling: if not isinstance(url, str) or not url.strip(): raise/handle.","Default missing config values: url = config.get('url') or '' and skip empty ones.","When iterating, unpack: for url in urls: md.convert_url(url) rather than passing the list."],"exampleFix":"// before\nresult = md.convert_url(payload.get(\"source_url\"))  # None when key missing\n\n// after\nurl = payload.get(\"source_url\")\nif not isinstance(url, str) or not url.strip():\n    raise ValueError(\"source_url missing\")\nresult = md.convert_url(url)","handlingStrategy":"type-guard","validationCode":"def valid_url_arg(url) -> bool:\n    return isinstance(url, str) and url.strip() != ''","typeGuard":"def is_non_empty_str(url: unknown) -> bool:\n    return typeof url === 'string' && url.trim().length > 0\n# Python:\ndef is_non_empty_str(url) -> bool:\n    return isinstance(url, str) and bool(url.strip())","tryCatchPattern":"try:\n    md.convert_url(url)\nexcept ValueError as e:\n    if \"non-empty string\" in str(e):\n        return None  # skip bad record\n    raise","preventionTips":["Validate user-supplied URLs at the API boundary before processing.","Use config.get('url') checks with early returns instead of passing None downstream."],"tags":["markitdown","url","input-validation"],"backgroundTag":null,"analyzedSha":"973504e1770928ed5fb82f43da528f441ad9ddc3","analyzedAt":"2026-08-14T23:45:32.408Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}