headroomlabs-ai/headroom · error · ImportError
Reading legacy .xls files requires xlrd. Install it with: pi
Error message
Reading legacy .xls files requires xlrd. Install it with: pip install headroom-ai[spreadsheet]
What it means
Reading legacy binary `.xls` (Excel 97-2003) workbooks requires the optional `xlrd` package, which is not installed. `xlrd` is kept separate from the base install (and from openpyxl, which only handles `.xlsx`), so legacy-format support is opt-in via the `spreadsheet` extra. Note the loader itself is marked as needing the optional dep plus a binary fixture, so it is exercised only lightly by the test suite.
Source
Thrown at headroom/transforms/spreadsheet_ingest.py:58
sheets: dict[str, str] = {}
try:
for ws in wb.worksheets:
rows = [list(r) for r in ws.iter_rows(values_only=True)]
text = _rows_to_csv(rows)
if text.strip():
sheets[ws.title] = text
finally:
wb.close()
return sheets
def _load_xls(
path: Path,
) -> dict[str, str]: # pragma: no cover - legacy .xls; needs optional xlrd + binary fixture
try:
import xlrd
except ImportError as e:
raise ImportError(
"Reading legacy .xls files requires xlrd. "
"Install it with: pip install headroom-ai[spreadsheet]"
) from e
book = xlrd.open_workbook(str(path))
sheets: dict[str, str] = {}
for sheet in book.sheets():
rows = [sheet.row_values(i) for i in range(sheet.nrows)]
text = _rows_to_csv(rows)
if text.strip():
sheets[sheet.name] = text
return sheets
def load_spreadsheet(path: str | Path) -> dict[str, str]:
"""Load a spreadsheet file into ``{sheet_name: csv_text}``.
Args:View on GitHub (pinned to 322425c43b)
Solutions
- Install the extra: `pip install headroom-ai[spreadsheet]` (it includes xlrd)
- Convert the workbook to `.xlsx` upstream (Excel/LibreOffice `--convert-to xlsx`, or pandas `read_excel` round-trip) if adding a dependency is undesirable
- Reject or quarantine `.xls` inputs with a clear pre-check if legacy format support is out of scope for your pipeline
Example fix
# before
$ pip install openpyxl
sheets = ingest_spreadsheet(Path("legacy_export.xls")) # ImportError
# after
$ pip install "headroom-ai[spreadsheet]"
sheets = ingest_spreadsheet(Path("legacy_export.xls")) Defensive patterns
Strategy: validation
Validate before calling
def xls_supported() -> bool:
try:
import xlrd # noqa: F401
return True
except ImportError:
return False
if path.suffix.lower() == ".xls" and not xls_supported():
raise RuntimeError("legacy .xls ingestion requires headroom-ai[spreadsheet]") Type guard
def can_read_xls() -> bool:
return importlib.util.find_spec("xlrd") is not None Try / catch
try:
sheets = ingest_spreadsheet(path)
except ImportError as e:
if "xlrd" in str(e):
# convert instead of installing: libreoffice --headless --convert-to xlsx
raise RuntimeError(".xls needs the spreadsheet extra or pre-conversion") from e
raise Prevention
- Install the [spreadsheet] extra wherever legacy Excel files may arrive
- Convert .xls to .xlsx at intake if you cannot add the dependency
- Reject .xls uploads with a clear message if legacy support is out of scope
When it happens
Trigger: Calling the ingest API with a path whose suffix is `.xls`, routing to `_load_xls`, in an environment where `import xlrd` fails.
Common situations: Ingesting exports from old enterprise systems that still emit `.xls`; installing only `openpyxl` (which since v2.x explicitly dropped `.xls` support) and assuming it covers all Excel formats; minimal CI images without the extra.
Related errors
- Reading .xlsx files requires openpyxl. Install it with: pip
- MCP SDK not installed. Install with: pip install mcp
- Error: Memory eval dependencies not installed.
- Error: Memory eval V2 dependencies not installed.
- Error: MCP SDK not installed.
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/fa4617ff275535a9.
Report an issue: GitHub.