HKUDS/Vibe-Trading · error · HTTPException

invalid alpha_id

Error message

invalid alpha_id

What it means

The tool caps how many tickers can be queried in one call via MAX_QUERY_STOCKS. If the normalized, deduped stock_ids list exceeds that cap, this ValueError is raised before any SQL runs, protecting the read-only snapshot from oversized queries.

Source

Thrown at agent/src/api/alpha_routes.py:456

                }
            )
        return {
            "status": "ok",
            "alphas": alphas,
            "total": total,
            "returned": len(alphas),
            "truncated": total > len(alphas),
        }

    # -----------------------------------------------------------------------
    # GET /alpha/{alpha_id}
    # -----------------------------------------------------------------------

    @app.get("/alpha/{alpha_id}", dependencies=[Depends(require_auth)])
    async def get_alpha(alpha_id: str) -> dict[str, Any]:
        """Return alpha metadata + the source code of its zoo .py file."""
        if not _ALPHA_ID_RE.fullmatch(alpha_id or ""):
            raise HTTPException(status_code=400, detail="invalid alpha_id")

        from src.factors.registry import RegistryError, get_default_registry

        registry = get_default_registry()
        try:
            alpha = registry.get(alpha_id)
        except KeyError:
            raise HTTPException(
                status_code=404,
                detail={"status": "error", "error": "alpha_id not found"},
            )

        try:
            source_code = registry.get_source(alpha_id)
        except RegistryError as exc:
            # Source-read failure is a degraded but recoverable case — log and
            # surface a short placeholder. The reason here is a typed registry
            # error (size cap or OS error from a known path), safe to expose.

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Chunk the list and loop: for chunk in batched(ids, MAX_QUERY_STOCKS): execute(action=..., stock_ids=chunk)
  2. Reduce the request to only the tickers you actually need
  3. Read MAX_QUERY_STOCKS from the module and size batches dynamically so cap changes don't break you

Example fix

# before
execute(action='latest', stock_ids=all_universe_ids)
# after
from itertools import islice
def chunked(seq, n):
    it=iter(seq)
    while (b:=list(islice(it,n))): yield b
results=[execute(action='latest', stock_ids=c) for c in chunked(ids, MAX_QUERY_STOCKS)]
Defensive patterns

Strategy: validation

Validate before calling

from agent.src.tools.taiwan_stock_data_tool import MAX_QUERY_STOCKS
ids = list(dict.fromkeys(ids))  # dedupe, order-preserving
assert len(ids) <= MAX_QUERY_STOCKS, f'cap is {MAX_QUERY_STOCKS}, got {len(ids)}'

Prevention

When it happens

Trigger: Calling execute(action='latest', stock_ids=[...]) with more than MAX_QUERY_STOCKS entries — e.g. batching an entire index membership (TWSE has 900+ listings) in one request instead of chunking.

Common situations: Feeding the output of action='universe' straight into lookup/latest; exporting a full portfolio from a spreadsheet; removing a previously applied chunking loop during refactoring.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/ba069a6b93edc885. Report an issue: GitHub.