ZhuLinsen/daily_stock_analysis · error · ValueError

Excel 解析失败: {e}。请确认:(1) 文件为 .xlsx 格式;(2) 工作表不为空;(3) 文件未损坏。若为

Error message

Excel 解析失败: {e}。请确认:(1) 文件为 .xlsx 格式;(2) 工作表不为空;(3) 文件未损坏。若为 .xls 格式,请另存为 .xlsx 后重试。

What it means

Raised when parsing bytes that carry the xlsx zip magic (PK\x03\x04) but openpyxl/pandas raises — i.e. a genuine broken Excel container. The hint distinguishes real xlsx corruption from a CSV merely named .xlsx (which falls back to text parsing with a warning instead).

Source

Thrown at src/services/import_parser.py:173

            # Use header=None to avoid silently consuming the first data row as column names
            # when the sheet has no header row. We detect headers the same way as the CSV path.
            df = pd.read_excel(io.BytesIO(data), sheet_name=0, engine="openpyxl", header=None, dtype=str)
            if df is None or df.empty:
                return []
            df = df.fillna("")
            first_row = [str(x).strip().lower() for x in df.iloc[0].tolist()]
            if any(c in _CODE_ALIASES or c in _NAME_ALIASES for c in first_row):
                df.columns = df.iloc[0]
                df = df.iloc[1:].reset_index(drop=True)
            return _parse_dataframe(df)
        except Exception as e:
            # If bytes strongly indicate xlsx container, treat as real Excel parse failure.
            if looks_like_zip:
                hint = (
                    "请确认:(1) 文件为 .xlsx 格式;(2) 工作表不为空;(3) 文件未损坏。"
                    "若为 .xls 格式,请另存为 .xlsx 后重试。"
                )
                raise ValueError(f"Excel 解析失败: {e}。{hint}") from e
            # For extension-only mismatch (e.g. csv named .xlsx), fallback to text parsing.
            logger.warning(f"扩展名为 .xlsx 但未解析为 Excel,将回退文本解析: {e}")

    # .xls not supported
    if ext == ".xls":
        raise ValueError("仅支持 .xlsx 格式,请将 .xls 另存为 .xlsx 后重试")

    # CSV / text
    for encoding in ("utf-8", "gbk"):
        try:
            text = data.decode(encoding)
            break
        except UnicodeDecodeError:
            continue
    else:
        raise ValueError("无法识别文件编码,请使用 UTF-8 或 GBK")

    # Single-column (one value per line): bypass pandas to avoid sep=None inference issues

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Re-open the file in Excel/WPS and re-save as .xlsx, then retry
  2. If it is really .xls, convert: save as .xlsx (the error hint says exactly this)
  3. Check the file wasn't truncated in transit — compare byte size with source
  4. If password-protected, remove protection before import

Example fix

// not applicable — file-level fix (re-save/convert), not a code fix
Defensive patterns

Strategy: try-catch

Validate before calling

def is_valid_xlsx(data: bytes) -> bool:
    import openpyxl, io
    try:
        openpyxl.load_workbook(io.BytesIO(data), read_only=True)
        return True
    except Exception:
        return False

Try / catch

try:
    items = parse_import_from_bytes(data, fn)
except ValueError as e:
    if 'Excel 解析失败' in str(e):
        prompt_user('请用 Excel 重新另存为 .xlsx 后再上传')
    else:
        raise

Prevention

When it happens

Trigger: Corrupted xlsx (truncated upload); password-protected or non-standard Excel zip; .xls (OLE2) is NOT this path — only zip-magic files. Empty worksheet also lands here.

Common situations: File renamed from another format; upload truncated by proxy/body-size limits; Excel file saved with unusual compression; xlsx exported by third-party tools with non-standard structure.

Related errors


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