ZhuLinsen/daily_stock_analysis · error · ValueError

validation_error

validation_error

Error message

symbol must not be empty

What it means

ValueError('symbol must not be empty') raised inside _resolve_position_analysis_context and mapped to a 400 validation_error by POST /portfolio/positions/{symbol}/analysis (portfolio.py:457-458). The path symbol is passed through PortfolioService._normalize_symbol_for_position; when normalization yields an empty string — empty/whitespace path segment, or a symbol consisting solely of characters normalization strips — the ValueError fires before any position lookup.

Source

Thrown at api/v1/endpoints/portfolio.py:503

    response = TaskAccepted(
        task_id=task.task_id,
        trace_id=task.trace_id or task.task_id,
        status="pending",
        message=f"分析任务已加入队列: {task.stock_code}",
        analysis_phase=task.analysis_phase,
    )
    return response


def _resolve_position_analysis_context(
    service: PortfolioService,
    *,
    symbol: str,
    account_id: Optional[int],
) -> dict:
    target = service._normalize_symbol_for_position(symbol)
    if not target:
        raise ValueError("symbol must not be empty")

    snapshot = service.get_portfolio_snapshot(account_id=account_id, cost_method="fifo")
    matches = []
    for account in snapshot.get("accounts") or []:
        for position in account.get("positions") or []:
            position_symbol = service._normalize_symbol_for_position(
                str(position.get("symbol") or "")
            )
            if position_symbol != target:
                continue
            try:
                quantity = float(position.get("quantity") or 0)
            except (TypeError, ValueError):
                quantity = 0.0
            if quantity <= 0:
                continue
            matches.append((account, position, position_symbol))

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Validate and trim the symbol client-side before POSTing; require a non-empty result.
  2. URL-encode the symbol: /portfolio/positions/{encodeURIComponent(symbol)}/analysis.
  3. If a legitimate symbol normalizes to empty, check _normalize_symbol_for_position's rules for that market's format (e.g. HK/A股 prefixes) and use the canonical form.

Example fix

# before
requests.post(f"{base}/portfolio/positions/{symbol}/analysis", json={})

# after
symbol = (symbol or '').strip()
if not symbol:
    raise ValueError('symbol is required')
requests.post(f"{base}/portfolio/positions/{quote(symbol)}/analysis", json={})
Defensive patterns

Strategy: validation

Validate before calling

const symbol = String(rawSymbol ?? '').trim();
if (!/^([0-9]{5,6}|[A-Z.\-]{1,12})$/i.test(symbol)) {
  throw new Error('symbol must be a non-empty ticker');
}
await fetch(`/portfolio/positions/${encodeURIComponent(symbol)}/analysis`, { method: 'POST', body: '{}' });

Type guard

const isNonEmptySymbol = (v: unknown): v is string =>
  typeof v === 'string' && v.trim().length > 0;

Try / catch

try { await analyzePosition(symbol); }
catch (e) { if (isHttp400(e) && /symbol must not be empty/.test(e.message)) flagBadSymbolInput(); }

Prevention

When it happens

Trigger: POST /portfolio/positions//analysis (empty path segment); a symbol of only spaces or only punctuation that normalization reduces to ''; URL-encoded whitespace (%20) as the symbol; client interpolating an unset variable into the path.

Common situations: Frontend submitting before the symbol state is populated; scripts iterating symbol lists containing blanks; copy/paste introducing invisible characters that normalize away.

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/75ad7700be3c8cb2. Report an issue: GitHub.