ruvnet/RuView · error · HTTPException

Health check failed: {str(e)}

Error message

Health check failed: {str(e)}

What it means

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.

Source

Thrown at archive/v1/src/api/routers/health.py:186

            )
            overall_status = "degraded"
        
        # Get system metrics
        system_metrics = await asyncio.to_thread(get_system_metrics)
        
        uptime_seconds = (datetime.now() - _APP_START_TIME).total_seconds()
        
        return SystemHealth(
            status=overall_status,
            timestamp=timestamp,
            uptime_seconds=uptime_seconds,
            components=components,
            system_metrics=system_metrics
        )
        
    except Exception as e:
        logger.error(f"Health check failed: {e}")
        raise HTTPException(
            status_code=500,
            detail=f"Health check failed: {str(e)}"
        )


@router.get("/ready", response_model=ReadinessCheck)
async def readiness_check(request: Request):
    """Check if system is ready to serve requests."""
    try:
        timestamp = datetime.utcnow()
        checks = {}
        
        # Check if services are available in app state
        if hasattr(request.app.state, 'pose_service') and request.app.state.pose_service:
            try:
                checks["pose_ready"] = await request.app.state.pose_service.is_ready()
            except Exception as e:
                logger.warning(f"Pose service readiness check failed: {e}")

View on GitHub (pinned to 4685618388)

Solutions

  1. Read the server log — the 'Health check failed:' line names the real exception
  2. Fix the failing dependency (DB URL, network, permissions for metrics)
  3. If you own the code, catch per-component and return a degraded status instead of letting one probe 500 the whole endpoint (see exampleFix)
  4. Point liveness probes at a cheaper endpoint (/ready or a root ping) if deep checks are too fragile

Example fix

# before
try:
    ...  # all component checks inline
except Exception as e:
    raise HTTPException(500, f'Health check failed: {str(e)}')

# after
components = {}
for name, check in checks.items():
    try:
        components[name] = check()
    except Exception as exc:
        components[name] = {'status': 'unhealthy', 'error': type(exc).__name__}
overall = 'unhealthy' if any(c['status'] == 'unhealthy' for c in components.values()) else 'healthy'
Defensive patterns

Strategy: fallback

Validate before calling

async def is_healthy(client):
    try:
        resp = await client.get('/health', timeout=5)
        return resp.status_code == 200 and resp.json().get('status') == 'healthy'
    except Exception:
        return False

Try / catch

try:
    resp = await client.get('/health', timeout=5.0)
    healthy = resp.status_code == 200
except (httpx.HTTPError, asyncio.TimeoutError):
    healthy = False
# treat any non-200 (including 500) as unhealthy — never parse the detail text

Prevention

When it happens

Trigger: 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: ...'.

Common situations: 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.

Related errors


AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16). Data as JSON: /api/errors/6ff13d67f00fb7df. Report an issue: GitHub.