{"record":{"id":"8de478b8c0fb11f4","repo":"lfnovo/open-notebook","slug":"invalid-url-format-check-server-logs-for-details","errorCode":null,"errorMessage":"Invalid URL format. Check server logs for details.","messagePattern":"Invalid URL format\\. Check server logs for details\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"open_notebook/utils/url_validation.py","lineNumber":108,"sourceCode":"                raise\n            # Not an IP address, it's a hostname - need to resolve and check\n            try:\n                # Resolve hostname to IP address. This is a blocking call -\n                # run it off the event loop so a slow/hanging DNS lookup\n                # doesn't stall every other concurrent request (this is\n                # called on the hot path of model provisioning, potentially\n                # once per chat message/transformation).\n                await _resolve_safe_ips(hostname)\n            except socket.gaierror:\n                # Could not resolve hostname - allow it since the URL may be\n                # valid in the deployment environment (e.g., Azure endpoints,\n                # internal DNS names). We only block link-local addresses.\n                pass\n\n    except ValueError:\n        raise\n    except Exception:\n        raise ValueError(\"Invalid URL format. Check server logs for details.\")\n\n\nasync def prepare_pinned_http_target(url: str, provider: str) -> PinnedHttpTarget:\n    \"\"\"\n    Validate ``url``, resolve DNS once, and pin the outbound target to a vetted IP.\n\n    Unlike ``validate_url`` alone (which still leaves a DNS-rebinding window\n    because httpx resolves again at connect time), this rewrites the request\n    URL to the vetted address and sets Host / ``sni_hostname`` so routing and\n    TLS verification keep the original hostname.\n\n    ``provider`` is accepted for call-site parity with ``validate_url``.\n\n    Raises:\n        ValueError: If the URL is invalid, resolves to a blocked address, or\n            cannot be resolved for an outbound request.\n    \"\"\"\n    if not url or not url.strip():","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/lfnovo/open-notebook/blob/a7de90d38aaf18ee85fd661854d35c11e44613e2/open_notebook/utils/url_validation.py#L90-L126","documentation":"This is a catch-all ValueError raised by validate_url when URL parsing fails in an unexpected way (anything that isn't already a ValueError, e.g. an exotic malformed URL that breaks urlparse or an IDNA/encoding issue). It signals the input could not be safely interpreted as a URL. The generic message intentionally hides details; the server log holds the underlying exception.","triggerScenarios":"Calling create_credential or update_credential with a malformed base_url; _build_content_state or _revalidate_config_urls running over stored config containing a corrupt URL string (e.g. 'http://exa mple.com', control characters, non-UTF8 bytes, or a port like 'http://host:99999999999').","commonSituations":"Typo'd provider URL pasted into the credential/config form; DB rows where the URL field contains whitespace, unicode homoglyphs, or was truncated; a URL with an invalid port or unbracketed IPv6.","solutions":["Check the server logs for the underlying exception to see what actually failed parsing","Normalize input before saving: url.strip() and reject empty or control-character strings","Fix or remove the malformed URL in the stored credential/config and retry","If it recurs with a seemingly valid URL, add a targeted log of the raw repr(url) to expose hidden characters"],"exampleFix":"// before\nawait create_credential(data={\"url\": \" http://api.example.com\\x00 \"})\n# after\nclean = url.strip().split(\"\\x00\")[0]\nif not clean.startswith((\"http://\", \"https://\")):\n    raise ValueError(\"URL must start with http:// or https://\")\nawait create_credential(data={\"url\": clean})","handlingStrategy":"try-catch","validationCode":"from urllib.parse import urlparse\n\ndef is_probably_valid_url(url: str) -> bool:\n    if not isinstance(url, str) or not url.strip():\n        return False\n    try:\n        p = urlparse(url.strip())\n    except Exception:\n        return False\n    return p.scheme in (\"http\", \"https\") and bool(p.hostname)","typeGuard":null,"tryCatchPattern":"try:\n    validate_url(url)\nexcept ValueError as e:\n    logger.warning(\"URL rejected (%r): %s\", url, e)\n    # surface a friendly form-field error, keep the raw repr out of logs if it may hold secrets\n    raise HTTPException(status_code=422, detail=str(e)) from e","preventionTips":["Strip and sanity-check URLs in the UI/form layer before they reach credentials or config storage","Reject URLs containing control characters or whitespace inside the authority","Log repr(url) at debug level when validation fails so invisible characters are discoverable"],"tags":["url-validation","valueerror","config"],"backgroundTag":"invalid-url","analyzedSha":"a7de90d38aaf18ee85fd661854d35c11e44613e2","analyzedAt":"2026-08-27T02:39:58.166Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}