{"record":{"id":"c4b498e3bdaeba54","repo":"ZhuLinsen/daily_stock_analysis","slug":"invalid-json","errorCode":"invalid_json","errorMessage":"JSON 解析失败: {e}","messagePattern":"JSON 解析失败: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"api/v1/endpoints/stocks.py","lineNumber":224,"sourceCode":"    summary=\"解析 CSV/Excel/剪贴板\",\n    description=\"上传 CSV/Excel 文件或粘贴文本，自动解析股票代码。文件上限 2MB，文本上限 100KB。\",\n)\nasync def parse_import(request: Request) -> ExtractFromImageResponse:\n    \"\"\"\n    解析 CSV/Excel 文件或剪贴板文本。\n\n    - multipart/form-data + file: 上传文件\n    - application/json + {\"text\": \"...\"}: 粘贴文本\n    - 优先使用 file，若同时提供则忽略 text\n    \"\"\"\n    content_type = (request.headers.get(\"content-type\") or \"\").lower()\n\n    if \"application/json\" in content_type:\n        try:\n            body = await request.json()\n        except Exception as e:\n            logger.warning(\"[parse_import] JSON parse failed: %s\", e)\n            raise HTTPException(\n                status_code=400,\n                detail={\"error\": \"invalid_json\", \"message\": f\"JSON 解析失败: {e}\"},\n            )\n        text = body.get(\"text\") if isinstance(body, dict) else None\n        if not text or not isinstance(text, str):\n            raise HTTPException(\n                status_code=400,\n                detail={\"error\": \"bad_request\", \"message\": \"未提供 text，请使用 {\\\"text\\\": \\\"...\\\"}\"},\n            )\n        try:\n            items = parse_import_from_text(text)\n        except ValueError as e:\n            text_bytes = len(text.encode(\"utf-8\"))\n            logger.warning(\n                \"[parse_import] parse_import_from_text failed: text_bytes=%d, error=%s\",\n                text_bytes,\n                e,\n            )","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/api/v1/endpoints/stocks.py#L206-L242","documentation":"Raised by POST /api/v1/stocks/parse-import when the request declares application/json but the body cannot be parsed by request.json(). Any JSONDecodeError-style failure is converted to HTTP 400 with error code invalid_json and the parser's message embedded; the raw parse error is also logged as a warning.","triggerScenarios":"Sending text/plain or form-encoded data with a JSON content-type header; a body with trailing commas, single quotes, unescaped newlines, or a BOM prefix; an empty body with Content-Type: application/json; truncated JSON from a proxy.","commonSituations":"Hand-built curl -d '{text: \"...\"}' (unquoted key, single quotes); pasting JSON containing literal newlines inside strings; Windows editors adding a UTF-8 BOM; JS sending a stringified object twice (JSON.stringify(JSON.stringify(x))).","solutions":["Validate the body with JSON.parse (JS) or json.loads (Python) before sending, and log the exact payload on failure.","Quote keys and use double quotes throughout: {\"text\": \"...\"}.","Strip any BOM and ensure Content-Type matches the actual body format."],"exampleFix":"# before\ncurl -X POST .../parse-import -H 'Content-Type: application/json' -d '{text: \"600519\"}'\n\n# after\ncurl -X POST .../parse-import -H 'Content-Type: application/json' -d '{\"text\": \"600519\"}'","handlingStrategy":"validation","validationCode":"let payload;\ntry { payload = JSON.parse(rawBodyText); } catch (e) {\n  showError('JSON 格式错误: ' + e.message); return;\n}\nawait fetch(url, {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(payload)});","typeGuard":"function isJsonObject(v: unknown): v is Record<string, unknown> {\n  if (typeof v !== 'object' || v === null || Array.isArray(v)) return false;\n  try { JSON.parse(JSON.stringify(v)); return true; } catch { return false; }\n}","tryCatchPattern":null,"preventionTips":["Always build request bodies with JSON.stringify of an object, never string concatenation.","Use curl -H 'Content-Type: application/json' with strict double-quoted JSON.","Strip BOMs from files used as request bodies."],"tags":["json","http-400","parse-import"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}