lfnovo/open-notebook · error · ValueError
Invalid URL format. Check server logs for details.
Error message
Invalid URL format. Check server logs for details.
What it means
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.
Source
Thrown at open_notebook/utils/url_validation.py:108
raise
# Not an IP address, it's a hostname - need to resolve and check
try:
# Resolve hostname to IP address. This is a blocking call -
# run it off the event loop so a slow/hanging DNS lookup
# doesn't stall every other concurrent request (this is
# called on the hot path of model provisioning, potentially
# once per chat message/transformation).
await _resolve_safe_ips(hostname)
except socket.gaierror:
# Could not resolve hostname - allow it since the URL may be
# valid in the deployment environment (e.g., Azure endpoints,
# internal DNS names). We only block link-local addresses.
pass
except ValueError:
raise
except Exception:
raise ValueError("Invalid URL format. Check server logs for details.")
async def prepare_pinned_http_target(url: str, provider: str) -> PinnedHttpTarget:
"""
Validate ``url``, resolve DNS once, and pin the outbound target to a vetted IP.
Unlike ``validate_url`` alone (which still leaves a DNS-rebinding window
because httpx resolves again at connect time), this rewrites the request
URL to the vetted address and sets Host / ``sni_hostname`` so routing and
TLS verification keep the original hostname.
``provider`` is accepted for call-site parity with ``validate_url``.
Raises:
ValueError: If the URL is invalid, resolves to a blocked address, or
cannot be resolved for an outbound request.
"""
if not url or not url.strip():View on GitHub (pinned to a7de90d38a)
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
Example fix
// before
await create_credential(data={"url": " http://api.example.com\x00 "})
# after
clean = url.strip().split("\x00")[0]
if not clean.startswith(("http://", "https://")):
raise ValueError("URL must start with http:// or https://")
await create_credential(data={"url": clean}) Defensive patterns
Strategy: try-catch
Validate before calling
from urllib.parse import urlparse
def is_probably_valid_url(url: str) -> bool:
if not isinstance(url, str) or not url.strip():
return False
try:
p = urlparse(url.strip())
except Exception:
return False
return p.scheme in ("http", "https") and bool(p.hostname) Try / catch
try:
validate_url(url)
except ValueError as e:
logger.warning("URL rejected (%r): %s", url, e)
# surface a friendly form-field error, keep the raw repr out of logs if it may hold secrets
raise HTTPException(status_code=422, detail=str(e)) from e Prevention
- 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
When it happens
Trigger: 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').
Common situations: 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.
Related errors
- {label} '{record_id}' not found
- {label} '{record_id}' has no content to embed
- Source '{input_data.source_id}' not found
- Source '{input_data.source_id}' has no text to embed
- No chunks created after splitting text
AI-assisted analysis of lfnovo/open-notebook@a7de90d38a (2026-08-27).
Data as JSON: /api/errors/8de478b8c0fb11f4.
Report an issue: GitHub.