ZhuLinsen/daily_stock_analysis · error · ValueError

仅支持 .xlsx 格式,请将 .xls 另存为 .xlsx 后重试

Error message

仅支持 .xlsx 格式,请将 .xls 另存为 .xlsx 后重试

What it means

Hard rejection in parse_import_from_bytes for files with .xls extension: the parser supports only .xlsx (openpyxl reads zip-based OOXML), not the legacy binary .xls (OLE2/BIFF) format.

Source

Thrown at src/services/import_parser.py:179

            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
    # e.g. "00700\n600519" or "code\n00700" - pandas with sep=None can produce wrong results
    lines = [ln.strip() for ln in text.strip().splitlines() if ln.strip()]
    if _should_use_single_column_fast_path(lines):
        rows = [[ln] for ln in lines]
        df = pd.DataFrame(rows)
        first_row = [str(x).strip().lower() for x in df.iloc[0].tolist()]

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Open the .xls in Excel/WPS and 'Save As' -> .xlsx, then re-upload
  2. Or copy the data into a CSV and upload that
  3. If automating, convert with LibreOffice headless: soffice --headless --convert-to xlsx file.xls

Example fix

$ soffice --headless --convert-to xlsx portfolio.xls
# then upload portfolio.xlsx
Defensive patterns

Strategy: validation

Validate before calling

if filename and filename.rsplit('.',1)[-1].lower() == 'xls':
    prompt_user('检测到 .xls,请另存为 .xlsx 或导出 CSV 后重试')

Type guard

def is_xls(filename: str) -> bool:
    return (filename or '').rsplit('.', 1)[-1].lower() == 'xls'

Prevention

When it happens

Trigger: Uploading a file named *.xls (legacy Excel 97-2003 format, or any file given that extension).

Common situations: Old broker exports still shipped as .xls; user renames .xls to .xlsx (which then takes the zip-magic fallback path, not this one); corporate templates in legacy format.

Related errors


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