ZhuLinsen/daily_stock_analysis · warning · HTTPException

invalid_stock_code

invalid_stock_code

Error message

股票代码不能为空

What it means

Raised by _validate_and_normalize_stock_code in the watchlist add/remove endpoints when the submitted stock_code is empty or only whitespace after stripping. It is a client-side validation error returned as HTTP 400 with error code invalid_stock_code. The check runs before any regex matching or persistence, so it never touches backend state.

Source

Thrown at api/v1/endpoints/stocks.py:98

    r"|(?:SH|SZ|BJ)\d{6}"                     # exchange-prefixed A-share
    r"|\d{6}\.(?:SH|SZ|SS|BJ)"                # exchange-suffixed A-share
    r"|\d{1,5}\.HK"                           # HK suffix format
    r"|HK\d{1,5}"                             # HK prefix format
    r"|\d{5}"                                 # bare 5-digit HK code
    r"|[A-Z]{1,5}(?:\.(?:US|[A-Z]))?"         # US ticker
    r")$",
    re.IGNORECASE,
)


def _validate_and_normalize_stock_code(code: str) -> str:
    """Validate stock code format and return canonical form.

    Raises HTTPException(400) if the code does not match supported formats.
    """
    stripped = code.strip()
    if not stripped:
        raise HTTPException(
            status_code=400,
            detail={"error": "invalid_stock_code", "message": "股票代码不能为空"},
        )
    if not _STOCK_CODE_RE.match(stripped):
        raise HTTPException(
            status_code=400,
            detail={
                "error": "invalid_stock_code",
                "message": f"'{stripped}' 不是合法的股票代码格式",
            },
        )
    return normalize_stock_code(stripped)


def _watchlist_match_key(code: str) -> str:
    """Return the equivalence key used for watchlist add/remove matching."""
    normalized = normalize_stock_code(code.strip())
    if re.fullmatch(r"\d{5}", normalized):

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Trim the input client-side and disable the submit action when stock_code.strip() is empty.
  2. Add a required-field check in the request schema (e.g. Pydantic min_length=1 with a strip validator) so FastAPI rejects it with 422 before the handler runs.
  3. If scripting, assert the variable is non-empty before calling the endpoint.

Example fix

// before
const code = codeInput.value;
await fetch(`/api/v1/stocks/watchlist/add`, {method:'POST', body: JSON.stringify({stock_code: code})});

// after
const code = codeInput.value.trim();
if (!code) { setFormError('请输入股票代码'); return; }
await fetch(`/api/v1/stocks/watchlist/add`, {method:'POST', body: JSON.stringify({stock_code: code})});
Defensive patterns

Strategy: validation

Validate before calling

const code = String(rawInput || '').trim();
if (!code) {
  showFormError('股票代码不能为空');
  return;
}
// safe to POST /watchlist/add

Type guard

function isNonEmptyStockCode(v: unknown): v is string {
  return typeof v === 'string' && v.trim().length > 0;
}

Prevention

When it happens

Trigger: POST /api/v1/stocks/watchlist/add or /watchlist/remove with body {"stock_code": ""} or {"stock_code": " "}; a frontend form submitted without user input; an automation script passing an unset variable that resolves to an empty string.

Common situations: Web form submit button enabled with an empty input field; JS code sending request.stock_code before assigning it; whitespace-only input pasted from a spreadsheet; default value of a dropdown/select left blank.

Related errors


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