ZhuLinsen/daily_stock_analysis · warning · HTTPException
bad_request
bad_request
Error message
未提供文件,请使用表单字段 file 上传图片
What it means
Raised by POST /api/v1/stocks/extract-from-image when the multipart form contains no field named file, or the field has no filename. The endpoint declares file as Optional[UploadFile] specifically so it can return this friendly 400 (bad_request) instead of FastAPI's default 422. It is purely a request-shape error; no bytes are read.
Source
Thrown at api/v1/endpoints/stocks.py:142
responses={
200: {"description": "提取的股票代码"},
400: {"description": "图片无效", "model": ErrorResponse},
500: {"description": "服务器错误", "model": ErrorResponse},
},
summary="从图片提取股票代码",
description="上传截图/图片,通过 Vision LLM 提取股票代码。支持 JPEG、PNG、WebP、GIF,最大 5MB。",
)
def extract_from_image(
file: Optional[UploadFile] = File(None, description="图片文件(表单字段名 file)"),
include_raw: bool = Query(False, description="是否在结果中包含原始 LLM 响应"),
) -> ExtractFromImageResponse:
"""
从上传的图片中提取股票代码(使用 Vision LLM)。
表单字段请使用 file 上传图片。优先级:Gemini / Anthropic / OpenAI(首个可用)。
"""
if not file or not file.filename:
raise HTTPException(
status_code=400,
detail={"error": "bad_request", "message": "未提供文件,请使用表单字段 file 上传图片"},
)
content_type = (file.content_type or "").split(";")[0].strip().lower()
if content_type not in ALLOWED_MIME:
raise HTTPException(
status_code=400,
detail={
"error": "unsupported_type",
"message": f"不支持的类型: {content_type}。允许: {ALLOWED_MIME_STR}",
},
)
try:
# 先读取限定大小,再检查是否还有剩余(语义清晰:超出则拒绝)
data = file.file.read(MAX_SIZE_BYTES)
if file.file.read(1):View on GitHub (pinned to 5159bd72e8)
Solutions
- Send multipart/form-data with the field named exactly 'file': curl -F 'file=@screenshot.png' .../extract-from-image.
- In JS, use formData.append('file', fileBlob, 'screenshot.png') so a filename is present.
- Double-check the endpoint: this route wants multipart, not application/json (that is /parse-import).
Example fix
// before
const fd = new FormData();
fd.append('image', file);
// after
const fd = new FormData();
fd.append('file', file, file.name); Defensive patterns
Strategy: validation
Validate before calling
if (!(file instanceof File) || !file.name) {
showError('请选择要上传的图片文件');
return;
}
const fd = new FormData();
fd.append('file', file, file.name); Type guard
function isUploadableFile(v: unknown): v is File {
return v instanceof File && typeof v.name === 'string' && v.name.length > 0;
} Prevention
- Always name the multipart field exactly 'file'.
- Disable the upload button until a file is selected.
- Include a filename when appending blobs: fd.append('file', blob, 'name.png').
When it happens
Trigger: Calling the endpoint with an empty body; sending the image under a different form field name (e.g. image, upload); sending JSON instead of multipart/form-data; some HTTP clients that omit filename in the Content-Disposition header.
Common situations: Frontend field name mismatch between the FormData append key and the API contract; curl invocations using -d instead of -F; automated tests posting JSON to a multipart endpoint; proxies stripping multipart parts.
Related errors
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/64ffd70a6361f18f.
Report an issue: GitHub.