ruvnet/RuView · error · HTTPException

Unknown service: {service_name}

Error message

Unknown service: {service_name}

What it means

check_service_health raises 400 'Unknown service: {service_name}' when the name it receives is not one of the three hardcoded names mapped to app.state attributes: 'pose' (pose_service), 'stream' (stream_service), or 'hardware' (hardware_service). It is a client-side input error, not a server health problem.

Source

Thrown at archive/v1/src/api/dependencies.py:266

    
    return router_id


# Service health dependencies
async def check_service_health(
    request: Request,
    service_name: str
) -> bool:
    """Check if a service is healthy."""
    try:
        if service_name == "pose":
            service = getattr(request.app.state, 'pose_service', None)
        elif service_name == "stream":
            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')}"
            )
        

View on GitHub (pinned to 4685618388)

Solutions

  1. Use exactly one of: pose, stream, hardware
  2. When adding a new service, extend the chain (or replace it with a dict registry name -> app.state attribute)
  3. Validate the parameter client-side before calling

Example fix

# before
if service_name == 'pose':
    service = getattr(request.app.state, 'pose_service', None)
elif ...
else:
    raise HTTPException(400, f'Unknown service: {service_name}')

# after: registry, so new services need one line
SERVICE_ATTRS = {'pose': 'pose_service', 'stream': 'stream_service', 'hardware': 'hardware_service'}
attr = SERVICE_ATTRS.get(service_name)
if attr is None:
    raise HTTPException(status_code=400, detail=f'Unknown service: {service_name}')
service = getattr(request.app.state, attr, None)
Defensive patterns

Strategy: type-guard

Type guard

KNOWN_SERVICES = frozenset({'pose', 'stream', 'hardware'})

def is_known_service(name: str) -> bool:
    """True when name is one of the services check_service_health maps to app.state."""
    return name in KNOWN_SERVICES

# guard before the call
if not is_known_service(service_name):
    raise ValueError(f'service must be one of {sorted(KNOWN_SERVICES)}, got {service_name!r}')

Try / catch

try:
    r = client.get(f'/api/health/{service_name}')
    r.raise_for_status()
except httpx.HTTPStatusError as e:
    if e.response.status_code == 400:
        # unknown name: fix the caller's vocabulary, do not retry
        raise ValueError(f'Unsupported service name: {service_name}') from e
    raise

Prevention

When it happens

Trigger: Calling the health dependency/route with service_name like 'inference', 'model', 'api', or a typo such as 'pose '; adding a new service to app.state without extending the if/elif chain in this function.

Common situations: Client and server disagreeing on service names; copy-pasted route parameters; new services registered at startup but forgotten in the health checker.

Related errors


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