unclecode/crawl4ai · error · HTTPException

Query parameter 'q' is required

Error message

Query parameter 'q' is required

What it means

A 400 from GET /llm/{url}: the required query parameter q (the question for LLM-based QA over the page) was missing, empty, or blank. The route declares q: str = Query(...) so FastAPI normally enforces presence, but an empty string ('?q=') passes FastAPI and is caught by this explicit `if not q` check.

Source

Thrown at deploy/docker/server.py:821

        raise HTTPException(500, detail=str(e))
    finally:
        if crawler:
            await release_crawler(crawler)


@app.get("/llm/{url:path}")
async def llm_endpoint(
    request: Request,
    url: str = Path(...),
    q: str = Query(...),
    provider: Optional[str] = Query(None, description="LLM provider override, e.g. 'openai/gpt-4o-mini'"),
    temperature: Optional[float] = Query(None, description="LLM temperature override"),
    _td: Dict = Depends(token_dep),
):
    # base_url is intentionally not accepted (key-exfil vector); the endpoint is
    # derived server-side from the provider name only.
    if not q:
        raise HTTPException(400, "Query parameter 'q' is required")
    if not url.startswith(("http://", "https://")) and not url.startswith(("raw:", "raw://")):
        url = "https://" + url
    answer = await handle_llm_qa(url, q, config, provider=provider, temperature=temperature)
    return JSONResponse({"answer": answer})


@app.get("/schema")
async def get_schema():
    from crawl4ai import BrowserConfig, CrawlerRunConfig
    return {"browser": BrowserConfig().dump(),
            "crawler": CrawlerRunConfig().dump()}


@app.get("/hooks/info")
async def get_hooks_info():
    """Enumerate the available declarative hook actions and their parameter schemas.

    Arbitrary hook code is no longer accepted (it was an exec()-based RCE

View on GitHub (pinned to 7e80152142)

Solutions

  1. Include a non-empty q: GET /llm/https://example.com/page?q=summarize%20the%20article.
  2. Client-side, refuse to call the endpoint when the question string is empty.
  3. If you see 422 instead, the parameter was absent entirely — same fix.

Example fix

# before
requests.get(f'{base}/llm/{url}')
# after
if not q.strip():
    raise ValueError('question required')
requests.get(f'{base}/llm/{url}', params={'q': q})
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlencode

def llm_url(base: str, url: str, q: str) -> str:
    if not q or not q.strip():
        raise ValueError("query 'q' must be a non-empty question")
    return f'{base}/llm/{url}?{urlencode({"q": q})}'

Type guard

def has_question(q: str | None) -> bool:
    return isinstance(q, str) and len(q.strip()) > 0

Prevention

When it happens

Trigger: GET /llm/https://example.com/page without a q parameter (FastAPI 422 normally) or with q= (empty value → this 400). Also q=%20 (whitespace-only still truthy, so passes — only truly empty values hit this).

Common situations: Clients building the query string manually and dropping q when the user asked no question; template strings leaving q empty; URL-encoding bugs that truncate parameters.

Related errors


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