{"record":{"id":"00541b81fb10e7af","repo":"NanmiCoder/MediaCrawler","slug":"no-crawler-is-running","errorCode":null,"errorMessage":"No crawler is running","messagePattern":"No crawler is running","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"api/routers/crawler.py","lineNumber":47,"sourceCode":"    \"\"\"Start crawler task\"\"\"\n    success = await crawler_manager.start(request)\n    if not success:\n        # Handle concurrent/duplicate requests: if process is already running, return 400 instead of 500\n        if crawler_manager.process and crawler_manager.process.poll() is None:\n            raise HTTPException(status_code=400, detail=\"Crawler is already running\")\n        raise HTTPException(status_code=500, detail=\"Failed to start crawler\")\n\n    return {\"status\": \"ok\", \"message\": \"Crawler started successfully\"}\n\n\n@router.post(\"/stop\")\nasync def stop_crawler():\n    \"\"\"Stop crawler task\"\"\"\n    success = await crawler_manager.stop()\n    if not success:\n        # Handle concurrent/duplicate requests: if process already exited/doesn't exist, return 400 instead of 500\n        if not crawler_manager.process or crawler_manager.process.poll() is not None:\n            raise HTTPException(status_code=400, detail=\"No crawler is running\")\n        raise HTTPException(status_code=500, detail=\"Failed to stop crawler\")\n\n    return {\"status\": \"ok\", \"message\": \"Crawler stopped successfully\"}\n\n\n@router.get(\"/status\", response_model=CrawlerStatusResponse)\nasync def get_crawler_status():\n    \"\"\"Get crawler status\"\"\"\n    return crawler_manager.get_status()\n\n\n@router.get(\"/logs\")\nasync def get_logs(limit: int = 100):\n    \"\"\"Get recent logs\"\"\"\n    logs = crawler_manager.logs[-limit:] if limit > 0 else crawler_manager.logs\n    return {\"logs\": [log.model_dump() for log in logs]}\n","sourceCodeStart":29,"sourceCodeEnd":64,"githubUrl":"https://github.com/NanmiCoder/MediaCrawler/blob/d6f7c5bb906b6dac40ddf343ef9e26438a3de092/api/routers/crawler.py#L29-L64","documentation":"Raised when the DevTools /json/version HTTP request completes but returns a non-200 status. It means something answered on the debug port, yet it is not a healthy CDP endpoint — the error text embeds the status code and body (note: the message is an un-expanded f-string in the raise site's metadata, but at runtime it carries the real status and response text).","triggerScenarios":"GET http://localhost:{debug_port}/json/version returning 404/500/503 — e.g. the port is occupied by a non-DevTools service, a proxy answered, or Chrome is still starting up and the DevTools handler is not yet mounted.","commonSituations":"debug_port typo or collision with another service; a system HTTP proxy intercepting localhost requests (httpx honors proxies env vars); querying the endpoint immediately after spawning Chrome before DevTools is listening; Chrome crashed at startup (bad flags) so an error page or nothing responds.","solutions":["Verify the endpoint manually: curl http://localhost:{debug_port}/json/version — a healthy browser returns JSON with Browser/webSocketDebuggerUrl fields","Confirm Chrome was launched with --remote-debugging-port={debug_port} and check the process is alive (ps aux | grep chrome)","Clear proxy env vars (HTTP_PROXY/HTTPS_PROXY/NO_PROXY) or build the async client with trust_env=False so localhost is never proxied","Retry with backoff for a few seconds after browser launch to ride out DevTools startup latency","Pick a different debug_port if another process owns it"],"exampleFix":"// before\nresponse = await client.get(\n    f\"http://localhost:{debug_port}/json/version\", timeout=10\n)\nif response.status_code == 200:\n    ...\nelse:\n    raise RuntimeError(f\"HTTP {response.status_code}: {response.text}\")\n\n// after\nimport asyncio\nfor _ in range(10):\n    response = await client.get(\n        f\"http://localhost:{debug_port}/json/version\",\n        timeout=10,\n        # never let a system proxy touch localhost\n        headers={\"Host\": \"localhost\"},\n    )\n    if response.status_code == 200:\n        return response.json().get(\"webSocketDebuggerUrl\")\n    await asyncio.sleep(1)\nraise RuntimeError(f\"HTTP {response.status_code}: {response.text}\")","handlingStrategy":"retry","validationCode":"async def wait_for_devtools(debug_port: int, attempts: int = 10, delay: float = 1.0) -> dict:\n    async with make_async_client(follow_redirects=True) as client:\n        for _ in range(attempts):\n            try:\n                resp = await client.get(f\"http://localhost:{debug_port}/json/version\", timeout=5)\n                if resp.status_code == 200:\n                    return resp.json()\n            except httpx.HTTPError:\n                pass\n            await asyncio.sleep(delay)\n    raise RuntimeError(f\"DevTools not healthy on port {debug_port}\")","typeGuard":null,"tryCatchPattern":"try:\n    version = await wait_for_devtools(port)\nexcept RuntimeError as e:\n    logger.error(f\"DevTools endpoint unhealthy: {e}\")\n    raise  # do not proceed with a half-initialized browser","preventionTips":["Health-check /json/version with retries before any connect attempt","Build localhost clients with trust_env=False (or clear proxy env vars) so corporate proxies never answer for localhost","Assert the debug port is free before launching Chrome to avoid collisions"],"tags":["cdp","http","chrome","devtools","proxy","port"],"backgroundTag":null,"analyzedSha":"d6f7c5bb906b6dac40ddf343ef9e26438a3de092","analyzedAt":"2026-08-15T01:39:07.505Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}