{"record":{"id":"ff8cd2199f5783a7","repo":"ZhuLinsen/daily_stock_analysis","slug":"max-file-bytes-1024-1024-mb","errorCode":null,"errorMessage":"文件超过 {MAX_FILE_BYTES // (1024 * 1024)}MB 限制","messagePattern":"文件超过 (.+?)MB 限制","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/services/import_parser.py","lineNumber":143,"sourceCode":"    return result\n\n\ndef parse_import_from_bytes(data: bytes, filename: Optional[str] = None) -> List[Tuple[Optional[str], Optional[str], str]]:\n    \"\"\"\n    Parse file bytes (CSV/Excel) into items.\n\n    Args:\n        data: File content bytes.\n        filename: Optional filename for format detection (e.g. \"a.csv\", \"b.xlsx\").\n\n    Returns:\n        List of (code, name, confidence); code may be None if resolution failed.\n\n    Raises:\n        ValueError: On parse error or unsupported format.\n    \"\"\"\n    if len(data) > MAX_FILE_BYTES:\n        raise ValueError(f\"文件超过 {MAX_FILE_BYTES // (1024 * 1024)}MB 限制\")\n\n    ext = \"\"\n    if filename:\n        ext = \".\" + filename.rsplit(\".\", 1)[-1].lower() if \".\" in filename else \"\"\n    logger.debug(f\"[ImportParser] 开始解析文件: filename={filename or '-'}, ext={ext or '-'}, bytes={len(data)}\")\n\n    looks_like_zip = len(data) >= 4 and data[:4] == b\"PK\\x03\\x04\"\n\n    # Excel: .xlsx (or zip magic)\n    if ext == \".xlsx\" or looks_like_zip:\n        try:\n            # Use header=None to avoid silently consuming the first data row as column names\n            # when the sheet has no header row. We detect headers the same way as the CSV path.\n            df = pd.read_excel(io.BytesIO(data), sheet_name=0, engine=\"openpyxl\", header=None, dtype=str)\n            if df is None or df.empty:\n                return []\n            df = df.fillna(\"\")\n            first_row = [str(x).strip().lower() for x in df.iloc[0].tolist()]","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/services/import_parser.py#L125-L161","documentation":"Guard in parse_import_from_bytes: uploaded file exceeds MAX_FILE_BYTES (2MB). Enforced before any format sniffing to bound parser memory use.","triggerScenarios":"Uploading a CSV/XLSX import file larger than 2MB; exporting a full watchlist with names and extra columns.","commonSituations":"Large broker exports; xlsx with embedded formatting/images inflating size; pasting whole portfolio history instead of the code column.","solutions":["Trim the file to only the needed columns (code, name) before import","For CSV, strip headers/extra columns; for xlsx, save as CSV of just the code column","Compress workflow: paste text instead (parse_import_from_text allows 100KB) or split into batches"],"exampleFix":"# before\nitems = parse_import_from_bytes(open('portfolio.xlsx','rb').read(), 'portfolio.xlsx')  # >2MB\n# after: extract just the code column to CSV first\nitems = parse_import_from_bytes(csv_only_bytes, 'portfolio.csv')","handlingStrategy":"validation","validationCode":"from src.services.import_parser import MAX_FILE_BYTES\nif len(data) > MAX_FILE_BYTES:\n    raise ValueError(f'文件过大，请精简到 {MAX_FILE_BYTES // (1024*1024)}MB 以内（仅保留代码/名称列）')","typeGuard":"def within_file_limit(b: bytes) -> bool:\n    return len(b) <= 2 * 1024 * 1024","tryCatchPattern":null,"preventionTips":["Show a 2MB limit in the import UI file picker","Pre-trim broker exports to the code/name columns","For big lists, paste text (100KB) in batches or split the file"],"tags":["validation","import","size-limit"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}