{"record":{"id":"6ff13d67f00fb7df","repo":"ruvnet/RuView","slug":"health-check-failed-str-e","errorCode":null,"errorMessage":"Health check failed: {str(e)}","messagePattern":"Health check failed: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"archive/v1/src/api/routers/health.py","lineNumber":186,"sourceCode":"            )\n            overall_status = \"degraded\"\n        \n        # Get system metrics\n        system_metrics = await asyncio.to_thread(get_system_metrics)\n        \n        uptime_seconds = (datetime.now() - _APP_START_TIME).total_seconds()\n        \n        return SystemHealth(\n            status=overall_status,\n            timestamp=timestamp,\n            uptime_seconds=uptime_seconds,\n            components=components,\n            system_metrics=system_metrics\n        )\n        \n    except Exception as e:\n        logger.error(f\"Health check failed: {e}\")\n        raise HTTPException(\n            status_code=500,\n            detail=f\"Health check failed: {str(e)}\"\n        )\n\n\n@router.get(\"/ready\", response_model=ReadinessCheck)\nasync def readiness_check(request: Request):\n    \"\"\"Check if system is ready to serve requests.\"\"\"\n    try:\n        timestamp = datetime.utcnow()\n        checks = {}\n        \n        # Check if services are available in app state\n        if hasattr(request.app.state, 'pose_service') and request.app.state.pose_service:\n            try:\n                checks[\"pose_ready\"] = await request.app.state.pose_service.is_ready()\n            except Exception as e:\n                logger.warning(f\"Pose service readiness check failed: {e}\")","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/ruvnet/RuView/blob/4685618388a5e49fad5b3005806f3bdd6a7c25c3/archive/v1/src/api/routers/health.py#L168-L204","documentation":"Raised as HTTP 500 by GET /health when any part of the composite health check throws. The handler wraps its whole body in one try/except and embeds the raw exception text in the detail, leaking internals, while the log line 'Health check failed: <e>' carries the same cause. A 500 here almost always means a probed dependency is broken, not that the HTTP app itself is dead.","triggerScenarios":"A component probe raises instead of returning a status — the database check throws because the DB is unreachable, a psutil/disk/metrics call fails, or any unexpected exception inside the handler becomes 500 'Health check failed: ...'.","commonSituations":"Misconfigured DATABASE_URL at deploy time; the process cannot read host metrics inside a minimal container; a downstream dependency is down and the orchestrator polling /health restarts the pod because the endpoint raises instead of reporting degraded.","solutions":["Read the server log — the 'Health check failed:' line names the real exception","Fix the failing dependency (DB URL, network, permissions for metrics)","If you own the code, catch per-component and return a degraded status instead of letting one probe 500 the whole endpoint (see exampleFix)","Point liveness probes at a cheaper endpoint (/ready or a root ping) if deep checks are too fragile"],"exampleFix":"# before\ntry:\n    ...  # all component checks inline\nexcept Exception as e:\n    raise HTTPException(500, f'Health check failed: {str(e)}')\n\n# after\ncomponents = {}\nfor name, check in checks.items():\n    try:\n        components[name] = check()\n    except Exception as exc:\n        components[name] = {'status': 'unhealthy', 'error': type(exc).__name__}\noverall = 'unhealthy' if any(c['status'] == 'unhealthy' for c in components.values()) else 'healthy'","handlingStrategy":"fallback","validationCode":"async def is_healthy(client):\n    try:\n        resp = await client.get('/health', timeout=5)\n        return resp.status_code == 200 and resp.json().get('status') == 'healthy'\n    except Exception:\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    resp = await client.get('/health', timeout=5.0)\n    healthy = resp.status_code == 200\nexcept (httpx.HTTPError, asyncio.TimeoutError):\n    healthy = False\n# treat any non-200 (including 500) as unhealthy — never parse the detail text","preventionTips":["Probe with short timeouts — a slow health check is a failed health check","Alert on state transitions, not every 500, to avoid storms during dependency outages","Keep liveness probes cheap; use readiness endpoints for deep dependency checks"],"tags":["python","fastapi","health-check","http-500","observability"],"backgroundTag":null,"analyzedSha":"4685618388a5e49fad5b3005806f3bdd6a7c25c3","analyzedAt":"2026-08-16T06:09:40.886Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}