{"record":{"id":"022f96463f39cf0a","repo":"unclecode/crawl4ai","slug":"nstproxy-api-error-data-get-msg-unknown-er","errorCode":null,"errorMessage":"[NSTProxy] API Error: {data.get('msg', 'Unknown error')}","messagePattern":"\\[NSTProxy\\] API Error: (.+?)","errorType":"exception","errorClass":"PermissionError","httpStatus":null,"severity":"error","filePath":"crawl4ai/async_configs.py","lineNumber":1092,"sourceCode":"            \"sessionDuration\": session_duration,\n            \"token\": token,\n        }\n        if state:\n            params[\"state\"] = state\n        if city:\n            params[\"city\"] = city\n\n        url = \"https://api.nstproxy.com/api/v1/generate/apiproxies\"\n\n        try:\n            response = requests.get(url, params=params, timeout=10)\n            response.raise_for_status()\n\n            data = response.json()\n\n            # --- Handle API error response ---\n            if isinstance(data, dict) and data.get(\"err\"):\n                raise PermissionError(f\"[NSTProxy] API Error: {data.get('msg', 'Unknown error')}\")\n\n            if not isinstance(data, list) or not data:\n                raise ValueError(\"[NSTProxy] Invalid API response — expected a non-empty list\")\n\n            proxy_info = data[0]\n\n            # --- Apply proxy config ---\n            self.proxy_config = ProxyConfig(\n                server=f\"{protocol}://{proxy_info['ip']}:{proxy_info['port']}\",\n                username=proxy_info[\"username\"],\n                password=proxy_info[\"password\"],\n            )\n\n        except Exception as e:\n            print(f\"[NSTProxy] ❌ Failed to set proxy: {e}\")\n            raise\n\nclass VirtualScrollConfig:","sourceCodeStart":1074,"sourceCodeEnd":1110,"githubUrl":"https://github.com/unclecode/crawl4ai/blob/7e801521428ee12509994d39151006f64055ebe3/crawl4ai/async_configs.py#L1074-L1110","documentation":"Raised as PermissionError when the NSTProxy API responds HTTP 200 but with a JSON object containing a truthy \"err\" field. The msg field carries the vendor's error text (auth failure, exhausted traffic, invalid channel, etc.). Note: this happens after response.raise_for_status(), so it is an application-level error, not a transport one.","triggerScenarios":"Expired or invalid NSTProxy token; wrong channel_id; account out of bandwidth/traffic; requesting a country not available on your plan — the API returns {\"err\": ..., \"msg\": \"...\"} with status 200.","commonSituations":"Old token committed to a repo after key rotation; free-plan limits hit mid-crawl; region restrictions on the account.","solutions":["Read the msg value in the exception — it names the exact vendor-side cause","Verify token and channel_id against the NSTProxy dashboard; regenerate the token if expired","Check remaining traffic/quota on your NSTProxy account and top up or reduce count","If the country/state/city combo is unsupported, retry with country=\"\" or a supported region"],"exampleFix":"// before\ncfg.set_nstproxy(token=old_token, channel_id=c)  # token rotated server-side\n// after\nfrom nstproxy_dashboard import fresh_token  # re-obtain\ncfg.set_nstproxy(token=fresh_token, channel_id=c)","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"from tenacity import retry, retry_if_exception_type, wait_exponential\n\n@retry(retry=retry_if_exception_type(PermissionError), wait=wait_exponential(min=2, max=30), stop=stop_after_attempt(3))\ndef setup_proxy(cfg, **kw):\n    cfg.set_nstproxy(**kw)\n# inspect str(e) for 'API Error' to distinguish vendor-side auth/quota failures","preventionTips":["Surface the vendor msg verbatim in logs","Alert on repeated auth errors — likely a rotated token","Keep token/traffic dashboards in monitoring"],"tags":["proxy","api","authentication","nstproxy","quota"],"backgroundTag":null,"analyzedSha":"7e801521428ee12509994d39151006f64055ebe3","analyzedAt":"2026-08-14T20:46:20.673Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}