{"record":{"id":"b171d48578a0a029","repo":"unclecode/crawl4ai","slug":"invalid-proxy-string-format-proxy-str","errorCode":null,"errorMessage":"Invalid proxy string format: {proxy_str}","messagePattern":"Invalid proxy string format: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"crawl4ai/async_configs.py","lineNumber":613,"sourceCode":"            if \":\" in credentials:\n                username, password = credentials.split(\":\", 1)\n                return ProxyConfig(\n                    server=f\"{protocol}://{server_part}\",\n                    username=username,\n                    password=password,\n                )\n        # URL without credentials (keep scheme)\n        if \"://\" in s and \"@\" not in s:\n            return ProxyConfig(server=s)\n        # Colon separated forms\n        parts = s.split(\":\")\n        if len(parts) == 4:\n            ip, port, username, password = parts\n            return ProxyConfig(server=f\"http://{ip}:{port}\", username=username, password=password)\n        if len(parts) == 2:\n            ip, port = parts\n            return ProxyConfig(server=f\"http://{ip}:{port}\")\n        raise ValueError(f\"Invalid proxy string format: {proxy_str}\")\n    \n    @staticmethod\n    def from_dict(proxy_dict: Dict) -> \"ProxyConfig\":\n        \"\"\"Create a ProxyConfig from a dictionary.\"\"\"\n        return ProxyConfig(\n            server=proxy_dict.get(\"server\"),\n            username=proxy_dict.get(\"username\"),\n            password=proxy_dict.get(\"password\"),\n            ip=proxy_dict.get(\"ip\"),\n        )\n    \n    @staticmethod\n    def from_env(env_var: str = \"PROXIES\") -> List[\"ProxyConfig\"]:\n        \"\"\"Load proxies from environment variable.\n        \n        Args:\n            env_var: Name of environment variable containing comma-separated proxy strings\n            ","sourceCodeStart":595,"sourceCodeEnd":631,"githubUrl":"https://github.com/unclecode/crawl4ai/blob/7e801521428ee12509994d39151006f64055ebe3/crawl4ai/async_configs.py#L595-L631","documentation":"EgressBlocked ('URL blocked') from resolve_and_pin's name-based pre-check (the pin path's mirror of assert_host_allowed): the hostname is blocklisted or starts with 'host.docker.internal'. Even though pinning happens in this function, container-internal names are refused by name before DNS to prevent reaching host services.","triggerScenarios":"Crawling (or following a redirect to) a URL whose host is in _BLOCKED_HOSTNAMES or is host.docker.internal*, while ALLOW_INTERNAL is off — check_redirect delegates here, so a redirect Location pointing at the docker host is equally blocked.","commonSituations":"A crawled page linking to host.docker.internal (common in apps that generate self-referential container URLs); redirects from a public site to an internal name during misconfigured deployments; blocklist additions catching previously allowed names.","solutions":["Serve the target through a public hostname instead of the docker-internal one","Fix the origin app so it emits externally routable self-URLs (public APP_URL/base-url setting)","Use an explicitly ALLOW_INTERNAL deployment for internal-target crawl jobs"],"exampleFix":"# before (app inside compose emits internal self-links)\nredirect Location: http://host.docker.internal:3000/login\n\n# after (configure the app's public base URL)\nredirect Location: https://app.example.com/login","handlingStrategy":"validation","validationCode":"def redirect_location_safe(location: str) -> bool:\n    h = (urlparse(location).hostname or \"\").lower()\n    return bool(h) and not h.startswith(\"host.docker.internal\") and h not in BLOCKED_HOSTNAMES","typeGuard":null,"tryCatchPattern":"from egress_broker import EgressBlocked, check_redirect\ntry:\n    target = check_redirect(location)\nexcept EgressBlocked:\n    stop_chain(location)  # never follow internal-name redirects","preventionTips":["Configure origin apps with a public base URL so self-links/redirects are external","Re-validate every redirect hop through check_redirect — later hops can go internal too","Keep container-internal hostnames out of any crawlable content"],"tags":["ssrf-protection","egress","redirect","blocked-hostname"],"backgroundTag":null,"analyzedSha":"7e801521428ee12509994d39151006f64055ebe3","analyzedAt":"2026-08-14T20:46:20.673Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}