{"record":{"id":"8dc17f276ed06877","repo":"D4Vinci/Scrapling","slug":"proxy-dict-must-have-a-server-key","errorCode":null,"errorMessage":"Proxy dict must have a 'server' key","messagePattern":"Proxy dict must have a 'server' key","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"scrapling/engines/toolbelt/proxy_rotation.py","lineNumber":79,"sourceCode":"            - Dict format: {\"server\": \"http://proxy:8080\", \"username\": \"user\", \"password\": \"pass\"}\n        :param strategy: Rotation strategy function. Takes (proxies, current_index) and returns (proxy, next_index). Defaults to cyclic_rotation.\n        \"\"\"\n        if not proxies:\n            raise ValueError(\"At least one proxy must be provided\")\n\n        if not callable(strategy):\n            raise TypeError(f\"strategy must be callable, got {type(strategy).__name__}\")\n\n        self._strategy = strategy\n        self._lock = Lock()\n\n        # Validate and store proxies\n        self._proxies: List[ProxyType] = []\n        self._proxy_to_index: Dict[str, int] = {}  # O(1) lookup by unique key (server + username)\n        for i, proxy in enumerate(proxies):\n            if isinstance(proxy, (str, dict)):\n                if isinstance(proxy, dict) and \"server\" not in proxy:\n                    raise ValueError(\"Proxy dict must have a 'server' key\")\n\n                self._proxy_to_index[_get_proxy_key(proxy)] = i\n                self._proxies.append(proxy)\n            else:\n                raise TypeError(f\"Invalid proxy type: {type(proxy)}. Expected str or dict.\")\n\n        self._current_index = 0\n\n    def get_proxy(self) -> ProxyType:\n        \"\"\"Get the next proxy according to the rotation strategy.\"\"\"\n        with self._lock:\n            proxy, self._current_index = self._strategy(self._proxies, self._current_index)\n            return proxy\n\n    @property\n    def proxies(self) -> List[ProxyType]:\n        \"\"\"Get a copy of all configured proxies.\"\"\"\n        return list(self._proxies)","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/D4Vinci/Scrapling/blob/5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f/scrapling/engines/toolbelt/proxy_rotation.py#L61-L97","documentation":"Raised during ProxyRotator construction when an element of the `proxies` list is a dict that lacks the required 'server' key. Playwright-style proxy dicts are keyed on 'server' (+ optional 'username'/'password'), and the rotator refuses dicts that omit it.","triggerScenarios":"ProxyRotator(proxies=[{'host': 'http://p:8080'}]), [{'server': ...}, {'username': 'u', 'password': 'p'}] (second entry missing server), or a dict produced by dropping empty values: {k: v for k, v in d.items() if v} where server was empty string.","commonSituations":"Mixed-quality proxy pools merged from multiple vendors where one source uses 'host'/'address' instead of 'server'; dict comprehensions that filter out falsy values; hand-written configs with typos in the key.","solutions":["Rename the key to 'server': {'server': 'http://p:8080', 'username': ..., 'password': ...}.","Normalize each dict before building the rotator: {'server': d.get('server') or d.get('host'), ...}.","Skip or log malformed entries while building the pool instead of passing them through."],"exampleFix":"# before\npool = [{\"host\": \"http://p1:8080\"}, \"http://p2:8080\"]\nrotator = ProxyRotator(pool)\n\n# after\npool = [{\"server\": \"http://p1:8080\"}, \"http://p2:8080\"]\nrotator = ProxyRotator(pool)","handlingStrategy":"validation","validationCode":"def has_server_key(p) -> bool:\n    return not isinstance(p, dict) or \"server\" in p\n\nbad = [p for p in pool if isinstance(p, dict) and \"server\" not in p]\nassert not bad, f\"proxy dicts missing 'server': {bad}\"","typeGuard":"def is_playwright_proxy_dict(value: object) -> bool:\n    return isinstance(value, dict) and isinstance(value.get(\"server\"), str)","tryCatchPattern":"try:\n    rotator = ProxyRotator(proxies=pool)\nexcept ValueError as e:\n    if \"'server' key\" in str(e):\n        pool = [p if not isinstance(p, dict) else {**{'server': p.get('server') or p.get('host', '')}, **p} for p in pool]\n        rotator = ProxyRotator(proxies=pool)\n    else:\n        raise","preventionTips":["Standardize every proxy dict to {'server', 'username', 'password'} on ingest.","Alias vendor keys (host/address) to 'server' in one normalization step.","Validate the whole pool before building the rotator."],"tags":["proxy","proxy-rotation","dict-schema","validation"],"backgroundTag":null,"analyzedSha":"5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f","analyzedAt":"2026-08-14T22:23:09.440Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}