unclecode/crawl4ai · warning · HTTPException

Invalid metric: {metric}. Must be one of: memory, requests,

Error message

Invalid metric: {metric}. Must be one of: memory, requests, browsers

What it means

An explicit 400 validation error raised by GET /monitor/timeline when the metric query parameter is not one of the three supported values: 'memory', 'requests', or 'browsers'. This is intentional allow-list validation of the metric dimension used to select which timeline series the monitor returns; anything else is rejected before any data access happens.

Source

Thrown at deploy/docker/monitor_routes.py:109

    try:
        monitor = get_monitor()
        return monitor.get_endpoint_stats_summary()
    except Exception as e:
        logger.error(f"Error getting endpoint stats: {e}")
        raise HTTPException(500, str(e))


@router.get("/timeline")
async def get_timeline(metric: str = "memory", window: str = "5m"):
    """Get timeline data for charts.

    Args:
        metric: 'memory', 'requests', or 'browsers'
        window: Time window (only '5m' supported for now)
    """
    # Input validation
    if metric not in ["memory", "requests", "browsers"]:
        raise HTTPException(400, f"Invalid metric: {metric}. Must be one of: memory, requests, browsers")
    if window != "5m":
        raise HTTPException(400, f"Invalid window: {window}. Only '5m' is currently supported")

    try:
        monitor = get_monitor()
        return monitor.get_timeline_data(metric, window)
    except Exception as e:
        logger.error(f"Error getting timeline: {e}")
        raise HTTPException(500, str(e))


@router.get("/logs/janitor")
async def get_janitor_log(limit: int = 100):
    """Get recent janitor cleanup events."""
    # Input validation
    if limit < 1 or limit > 1000:
        raise HTTPException(400, f"Invalid limit: {limit}. Must be between 1 and 1000")

View on GitHub (pinned to 7e80152142)

Solutions

  1. Use one of the exact supported values: metric=memory, metric=requests, or metric=browsers (they are case-sensitive).
  2. If a dashboard needs another metric, extend the allow-list and monitor.get_timeline_data() server-side first, then update the client.
  3. Add a client-side enum/dropdown constrained to the three values so invalid requests cannot be issued.

Example fix

# before
const url = `/monitor/timeline?metric=${selected}`; // selected = 'cpu'

# after
const ALLOWED = ['memory', 'requests', 'browsers'];
const metric = ALLOWED.includes(selected) ? selected : 'memory';
const url = `/monitor/timeline?metric=${metric}&window=5m`;
Defensive patterns

Strategy: validation

Validate before calling

ALLOWED_METRICS = {'memory', 'requests', 'browsers'}

def valid_metric(m: str) -> bool:
    return m in ALLOWED_METRICS

url = f"/monitor/timeline?metric={m if valid_metric(m) else 'memory'}"

Type guard

from typing import Literal

Metric = Literal['memory', 'requests', 'browsers']

def is_metric(v: str) -> TypeGuard[Metric]:
    return v in ('memory', 'requests', 'browsers')

Try / catch

try:
    resp = get(f"{base}/monitor/timeline?metric={metric}")
except HTTPError as e:
    if e.response.status_code == 400:
        raise ValueError(f'unsupported metric {metric!r}; use memory|requests|browsers') from e
    raise

Prevention

When it happens

Trigger: Calling GET /monitor/timeline?metric=cpu or ?metric=mem (typo/abbreviation), passing an empty value (?metric=), or a dashboard passing a newer metric name the server does not support yet.

Common situations: Frontend chart component built against a newer API that added metrics (e.g. 'latency') while the deployed server only supports three; URL-encoding issues producing an unexpected string; copy-paste from docs that list a metric not yet implemented.

Related errors


AI-assisted analysis of unclecode/crawl4ai@7e80152142 (2026-08-14). Data as JSON: /api/errors/e3e3c1e3d01024f8. Report an issue: GitHub.