ruvnet/RuView · error · HTTPException
Service '{service_name}' is unhealthy: {status_info.get('err
Error message
Service '{service_name}' is unhealthy: {status_info.get('error', 'Unknown error')} What it means
check_service_health raises 503 "Service '<name>' is unhealthy: <error>" when the service object exists and its get_status() call succeeds but reports status != 'healthy'. The underlying error text from the service's own status dict is forwarded into the message, making this the informative variant of the two 503s.
Source
Thrown at archive/v1/src/api/dependencies.py:280
service = getattr(request.app.state, 'stream_service', None)
elif service_name == "hardware":
service = getattr(request.app.state, 'hardware_service', None)
else:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Unknown service: {service_name}"
)
if not service:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail=f"Service '{service_name}' not available"
)
# Check service health
status_info = await service.get_status()
if status_info.get("status") != "healthy":
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail=f"Service '{service_name}' is unhealthy: {status_info.get('error', 'Unknown error')}"
)
return True
except HTTPException:
raise
except Exception as e:
logger.error(f"Error checking service health for {service_name}: {e}")
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail=f"Service '{service_name}' health check failed"
)
# Rate limiting dependencies
async def check_rate_limit(View on GitHub (pinned to 4685618388)
Solutions
- Read the error text embedded in the detail — it is the service's own get_status() error and names the failing dependency
- Restore the named dependency (model backend, stream source, hardware device) and re-check
- Check the service's logs for the same error to get the full traceback
- Keep readiness probes pointed at this endpoint so traffic stops while unhealthy
Defensive patterns
Strategy: retry
Try / catch
try:
r = client.get(f'/api/health/{service_name}')
r.raise_for_status()
except httpx.HTTPStatusError as e:
detail = e.response.json().get('detail', '')
if e.response.status_code == 503 and 'is unhealthy' in detail:
# detail carries the service's own error: log it, back off, retry
logger.warning('service degraded: %s', detail)
await asyncio.sleep(backoff)
r = client.get(f'/api/health/{service_name}')
raise Prevention
- Parse the embedded error text from the 503 detail — it names the failing dependency
- Wire readiness probes to this endpoint so unhealthy services stop receiving traffic
- Alert on sustained 'unhealthy' rather than single blips
When it happens
Trigger: The pose service reporting degraded because its model/inference backend is unreachable; the stream service unhealthy because no CSI feed is connected; the hardware service failing its device probe — each returned by service.get_status() with status 'unhealthy'/'degraded' rather than 'healthy'.
Common situations: Backend dependencies down (model server, CSI collector, serial device); post-crash services that restarted but have not re-validated their dependencies; partial infra outages.
Related errors
- Service '{service_name}' not available
- Service '{service_name}' health check failed
- Unknown service: {service_name}
- Health check failed: {str(e)}
- JWT authentication is not configured. In development mode, e
AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16).
Data as JSON: /api/errors/731dc5eaa24759fc.
Report an issue: GitHub.