ruvnet/RuView · warning · HTTPException

Client {client_id} not found

Error message

Client {client_id} not found

What it means

Deliberate 404 raised by DELETE /stream/clients/{client_id} in archive/v1/src/api/routers/stream.py when connection_manager.disconnect(client_id) returns False — meaning no active connection matches the given id. Unlike the surrounding 500 catch-alls, this is a controlled, expected outcome of a racing or stale request, not an internal failure.

Source

Thrown at archive/v1/src/api/routers/stream.py:448

        raise HTTPException(
            status_code=500,
            detail="An internal error occurred. Please try again later."
        )


@router.delete("/clients/{client_id}")
async def disconnect_client(
    client_id: str,
    current_user: Dict = Depends(require_auth)
):
    """Disconnect a specific WebSocket client."""
    try:
        logger.info(f"Disconnecting client {client_id} by user: {current_user['id']}")
        
        success = await connection_manager.disconnect(client_id)
        
        if not success:
            raise HTTPException(
                status_code=404,
                detail=f"Client {client_id} not found"
            )
        
        return {
            "message": f"Client {client_id} disconnected successfully",
            "timestamp": datetime.utcnow().isoformat()
        }
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error disconnecting client: {e}")
        raise HTTPException(
            status_code=500,
            detail="An internal error occurred. Please try again later."
        )

View on GitHub (pinned to 4685618388)

Solutions

  1. Re-fetch GET /stream/clients and confirm the id is still present before retrying
  2. Treat 404 as success when the goal is only 'make sure this client is gone'
  3. Copy ids verbatim from the current clients listing; never construct them by hand or from stale logs
  4. If the id should exist, compare it byte-for-byte with the listing (encoding, whitespace, case) to rule out a formatting mismatch

Example fix

# before (caller treats 404 as an error)
resp = requests.delete(f"{API}/stream/clients/{client_id}")
resp.raise_for_status()

# after (caller treats 404 as already-gone)
resp = requests.delete(f"{API}/stream/clients/{client_id}")
if resp.status_code == 404:
    logger.info(f"client {client_id} already disconnected")
else:
    resp.raise_for_status()
Defensive patterns

Strategy: type-guard

Validate before calling

import httpx

listing = httpx.get(f"{API}/stream/clients", headers=auth, timeout=10).json()
known_ids = {c["id"] for c in listing["clients"]}
if client_id not in known_ids:
    print(f"{client_id} not connected; nothing to disconnect")

Type guard

def client_connected(client_id: str, listing: dict) -> bool:
    return any(c.get("id") == client_id for c in listing.get("clients", []))

Try / catch

import httpx
try:
    resp = httpx.delete(f"{API}/stream/clients/{client_id}", headers=auth, timeout=10)
    resp.raise_for_status()
except httpx.HTTPStatusError as e:
    if e.response.status_code == 404:
        pass  # already gone — treat as success
    else:
        raise

Prevention

When it happens

Trigger: DELETE /stream/clients/{client_id} with an id that already disconnected; using a stale id captured from an earlier GET /stream/clients listing; a double-click in an admin UI sending the delete twice, where the second call finds nothing; id formatting mismatch (whitespace, URL-encoding, wrong case) between the listing and the path parameter.

Common situations: Race between listing clients and disconnecting one (client drops itself in between); UI not refreshing the client list after a disconnect; copy-pasting ids from logs of a previous session instead of the current GET /stream/clients response.

Related errors


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