{"record":{"id":"7bde635949e87fec","repo":"NanmiCoder/MediaCrawler","slug":"not-invalid-kuaidaili-proxy-info","errorCode":null,"errorMessage":"not invalid kuaidaili proxy info","messagePattern":"not invalid kuaidaili proxy info","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"proxy/providers/kuaidl_proxy.py","lineNumber":58,"sourceCode":"\nclass KuaidailiProxyModel(BaseModel):\n    ip: str = Field(\"ip\")\n    port: int = Field(\"port\")\n    expire_ts: int = Field(\"Expiration time, in seconds, how many seconds until expiration\")\n\n\ndef parse_kuaidaili_proxy(proxy_info: str) -> KuaidailiProxyModel:\n    \"\"\"\n    Parse KuaiDaili IP information\n    Args:\n        proxy_info:\n\n    Returns:\n\n    \"\"\"\n    proxies: List[str] = proxy_info.split(\":\")\n    if len(proxies) != 2:\n        raise Exception(\"not invalid kuaidaili proxy info\")\n\n    pattern = r'(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}):(\\d{1,5}),(\\d+)'\n    match = re.search(pattern, proxy_info)\n    if not match.groups():\n        raise Exception(\"not match kuaidaili proxy info\")\n\n    return KuaidailiProxyModel(\n        ip=match.groups()[0],\n        port=int(match.groups()[1]),\n        expire_ts=int(match.groups()[2])\n    )\n\n\nclass KuaiDaiLiProxy(ProxyProvider):\n    def __init__(self, kdl_user_name: str, kdl_user_pwd: str, kdl_secret_id: str, kdl_signature: str):\n        \"\"\"\n\n        Args:","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/NanmiCoder/MediaCrawler/blob/d6f7c5bb906b6dac40ddf343ef9e26438a3de092/proxy/providers/kuaidl_proxy.py#L40-L76","documentation":"Thrown by parse_kuaidaili_proxy() when the proxy string returned by the KuaiDaiLi API cannot be split on ':' into exactly two parts. The expected wire format is 'IP:PORT,EXPIRE_SECONDS' (e.g. '1.2.3.4:8080,120'), so exactly one colon is allowed. Any deviation — extra colons, a missing port, an empty string — fails this pre-check before the regex stage.","triggerScenarios":"Calling KuaiDaiLiProxy.get_proxies() and the provider's data.proxy_list entries arrive as 'user:pass@ip:port,expire' (credential-prefixed format from a tunnel/order type change) or as bare 'ip:port' without the trailing ',expire' field. Also triggered if the API response format changes or the entry is an empty string.","commonSituations":"Switching KuaiDaiLi order types (exclusive tunnel vs API extraction) changes the proxy_list entry shape; a KuaiDaiLi API version change; mistakenly feeding a proxy string from a different provider (e.g. Wandou format) into this parser.","solutions":["Verify the raw value: log response.json()['data']['proxy_list'] and confirm each entry looks like '1.2.3.4:8080,120'","If entries carry credentials ('user:pass@ip:port,ttl'), switch the KuaiDaiLi order type back to API extraction or strip the credential prefix before parsing","If the entry lacks ',expire', the API order/params are wrong — check the 'pt' and 'format' params sent in self.params for the GetKdlIp endpoint","Normalize the input before calling: split on the last ':' and re-join so only ip:port,expire remains"],"exampleFix":"// before\nproxies: List[str] = proxy_info.split(\":\")\nif len(proxies) != 2:\n    raise Exception(\"not invalid kuaidaili proxy info\")\n\n// after (tolerate credential-prefixed format)\ninfo = proxy_info.split(\"@\")[-1]  # strip user:pass@ prefix if present\nhost_part, _, expire = info.partition(\",\")\nif \",\" not in info or \":\" not in host_part:\n    raise ValueError(f\"not invalid kuaidaili proxy info: {proxy_info!r}\")","handlingStrategy":"validation","validationCode":"import re\n\ndef is_valid_kuaidaili_proxy(s: str) -> bool:\n    if not isinstance(s, str) or s.count(\":\") != 1:\n        return False\n    return re.fullmatch(r\"(\\d{1,3}\\.){3}\\d{1,3}:\\d{1,5},\\d+\", s) is not None\n\n# before parsing:\n# assert is_valid_kuaidaili_proxy(entry), f\"bad kdl entry {entry!r}\"","typeGuard":"def is_kuaidaili_proxy_str(s: str) -> bool:\n    return (\n        isinstance(s, str)\n        and s.count(\":\") == 1\n        and re.fullmatch(r\"(\\d{1,3}\\.){3}\\d{1,3}:\\d{1,5},\\d+\", s) is not None\n    )","tryCatchPattern":"try:\n    model = parse_kuaidaili_proxy(entry)\nexcept (Exception,) as e:\n    utils.logger.error(f\"skipping malformed kdl proxy {entry!r}: {e}\")\n    continue  # skip bad entries, keep the rest of the batch","preventionTips":["Log raw proxy_list entries once per fetch so format regressions are visible immediately","Pin the KuaiDaiLi order type (API extraction) so the wire format stays 'ip:port,ttl'","Add a contract test for parse_kuaidaili_proxy with the documented format"],"tags":["proxy","kuaidaili","parsing","api-contract"],"backgroundTag":null,"analyzedSha":"d6f7c5bb906b6dac40ddf343ef9e26438a3de092","analyzedAt":"2026-08-15T01:39:07.505Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}