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

  1. Read the error text embedded in the detail — it is the service's own get_status() error and names the failing dependency
  2. Restore the named dependency (model backend, stream source, hardware device) and re-check
  3. Check the service's logs for the same error to get the full traceback
  4. 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

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


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