{"record":{"id":"aae2c2e1f95b9e93","repo":"mlflow/mlflow","slug":"invalid-webhook-url-url-r-e-r","errorCode":null,"errorMessage":"Invalid webhook URL {url!r}: {e!r}","messagePattern":"Invalid webhook URL (.+?): (.+?)","errorType":"validation","errorClass":"MlflowException","httpStatus":400,"severity":"error","filePath":"mlflow/utils/validation.py","lineNumber":1151,"sourceCode":"\n    _validate_mcp_list_max_length(tools, field_name, _MAX_MCP_TOOLS_PER_LIST)\n\n\ndef _validate_webhook_url(url: str) -> None:\n    if not isinstance(url, str):\n        raise MlflowException.invalid_parameter_value(\n            f\"Webhook URL must be a string, got {type(url).__name__!r}\"\n        )\n\n    if not url.strip():\n        raise MlflowException.invalid_parameter_value(\n            f\"Webhook URL cannot be empty or just whitespace: {url!r}\"\n        )\n\n    try:\n        parsed_url = urllib.parse.urlparse(url)\n    except ValueError as e:\n        raise MlflowException.invalid_parameter_value(f\"Invalid webhook URL {url!r}: {e!r}\") from e\n    schemes = _MLFLOW_WEBHOOK_ALLOWED_SCHEMES.get()\n    if parsed_url.scheme not in schemes:\n        raise MlflowException.invalid_parameter_value(\n            f\"Invalid webhook URL scheme: {parsed_url.scheme!r}. \"\n            f\"Allowed schemes are: {', '.join(schemes)}.\"\n        )\n\n    hostname = parsed_url.hostname\n    if not hostname:\n        raise MlflowException.invalid_parameter_value(\n            f\"Webhook URL must include a hostname: {url!r}\"\n        )\n\n    if not _MLFLOW_WEBHOOK_ALLOW_PRIVATE_IPS.get():\n        _validate_hostname_resolves_to_public_ips(hostname, \"Webhook URL\")\n\n\ndef _validate_webhook_events(events: list[WebhookEvent]) -> None:","sourceCodeStart":1133,"sourceCodeEnd":1169,"githubUrl":"https://github.com/mlflow/mlflow/blob/6a27f2decc0b76eb1b54af31849784addb357dbc/mlflow/utils/validation.py#L1133-L1169","documentation":"Raised by _validate_webhook_url when urllib.parse.urlparse raises ValueError on the given URL string, meaning the URL is syntactically unparseable (typically malformed IPv6 literals like 'http://[::1' or characters outside what urlparse accepts). MLflow wraps that underlying ValueError into an MlflowException.invalid_parameter_value with the original URL and error repr.","triggerScenarios":"Calling create_webhook, update_webhook, or test_validate_webhook_url with a structurally malformed URL such as 'http://[::1' (unclosed IPv6 bracket) or a URL containing characters that make urlparse raise; _send_webhook_request validating a stored URL that was corrupted.","commonSituations":"Hand-edited webhook URLs with a typo (missing bracket, stray control character); programmatic URL construction with unescaped/unencoded parts; URLs stored before validation in older versions then hit during delivery; copying a URL that got truncated or mangled by a shell.","solutions":["Inspect the URL in the message for structural typos (e.g. unclosed '[' in IPv6 hosts) and fix it to a well-formed URL.","Validate locally first: python -c \"import urllib.parse; urllib.parse.urlparse('<your url>')\" to reproduce and debug before calling the API.","If building URLs programmatically, use urllib.parse.urlunparse or urlencode to construct them safely.","If the malformed URL is already stored, call update_webhook with a corrected URL to replace it."],"exampleFix":"// before\nurl = \"https://hooks.example.com/notify?data={a b}\"  # malformed\nclient.create_webhook(name=\"w\", url=url, events=[...])\n\n// after\nimport urllib.parse\nurl = urllib.parse.urlunparse((\"https\", \"hooks.example.com\", \"/notify\", \"\",\n                               urllib.parse.urlencode({\"data\": \"a b\"}), \"\"))\nclient.create_webhook(name=\"w\", url=url, events=[...])","handlingStrategy":"validation","validationCode":"import urllib.parse\n\ndef validate_url_parseable(url):\n    try:\n        urllib.parse.urlparse(url)\n    except ValueError as e:\n        raise ValueError(f\"Malformed webhook URL {url!r}: {e}\") from e","typeGuard":"def is_parseable_url(url):\n    if not isinstance(url, str):\n        return False\n    try:\n        urllib.parse.urlparse(url)\n        return True\n    except ValueError:\n        return False","tryCatchPattern":"from mlflow.exceptions import MlflowException\ntry:\n    client.create_webhook(name=name, url=url, events=events)\nexcept MlflowException as e:\n    if \"Invalid webhook URL\" in str(e):\n        url = fix_or_prompt_for_url(url)  # e.g. repair IPv6 brackets / re-encode\n        client.create_webhook(name=name, url=url, events=events)\n    else:\n        raise","preventionTips":["Run urlparse on URLs before storing or sending them to catch syntax errors early.","Build URLs with urllib.parse.urlunparse/urlencode instead of string concatenation.","Beware IPv6 literals: brackets must be balanced, e.g. http://[::1]:8080/hook.","Round-trip check stored webhook URLs (parse then unparse) during config load to catch corruption."],"tags":["webhook","validation","url","malformed-url","urlparse"],"backgroundTag":"malformed-url","analyzedSha":"6a27f2decc0b76eb1b54af31849784addb357dbc","analyzedAt":"2026-08-29T20:54:51.419Z","schemaVersion":2},"datasetVersion":"2026-08-29T22:17:34.462Z"}