ruvnet/RuView · error · HTTPException

Access denied to router '{router_id}'

Error message

Access denied to router '{router_id}'

What it means

validate_router_access raises 403 "Access denied to router '<router_id>'" when the authenticated user is not an admin, their routers list is non-empty, and the requested router is not in it. As with zones, an empty routers list means unrestricted; denial requires a non-empty list that excludes the router.

Source

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

        )
    
    # Check if router is enabled
    if not router.enabled:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail=f"Router '{router_id}' is disabled"
        )
    
    # If authentication is enabled, check user access
    if current_user:
        # Admin users have access to all routers
        if current_user.get("is_admin", False):
            return router_id
        
        # Check user's router permissions
        user_routers = current_user.get("routers", [])
        if user_routers and router_id not in user_routers:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"Access denied to router '{router_id}'"
            )
    
    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)

View on GitHub (pinned to 4685618388)

Solutions

  1. Add the router to the user's routers allowlist (or have an admin do it)
  2. Use an admin token, which bypasses the router allowlist
  3. If the user should access all routers, clear their routers list
Defensive patterns

Strategy: validation

Validate before calling

# Check the token's router allowlist before the call (empty list = unrestricted)
claims = jwt.decode(token, options={'verify_signature': False})
routers = claims.get('routers') or []
if routers and router_id not in routers and not claims.get('is_admin'):
    raise PermissionError(f'Token not scoped for router {router_id}')

Try / catch

try:
    r = client.get(f'/api/routers/{router_id}', headers=auth)
    r.raise_for_status()
except httpx.HTTPStatusError as e:
    if e.response.status_code == 403 and 'Access denied' in e.response.text:
        request_router_access(router_id)
    raise

Prevention

When it happens

Trigger: A user provisioned with routers=['node-a'] calling a route for 'node-b'; scoped service accounts whose allowlist predates a newly added router; stale tokens after router provisioning changes.

Common situations: Per-node access scoping for field technicians; adding hardware without updating user allowlists; token claims drifting from current permissions.

Understand the failure class

Related errors


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