{"record":{"id":"f0adfc8a615416cd","repo":"pola-rs/polars","slug":"file-source-name-r-must-be-opened-in-binary-mode","errorCode":null,"errorMessage":"file {source.name!r} must be opened in binary mode","messagePattern":"file (.+?) must be opened in binary mode","errorType":"exception","errorClass":"OSError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/io/spreadsheet/functions.py","lineNumber":884,"sourceCode":"\n    elif engine == \"calamine\":\n        fastexcel = import_optional(\"fastexcel\", min_version=\"0.7.0\")\n        reading_bytesio, reading_bytes = (\n            isinstance(source, BytesIO),\n            isinstance(source, bytes),\n        )\n        if (reading_bytesio or reading_bytes) and parse_version(\n            module_version := fastexcel.__version__\n        ) < (0, 10):\n            msg = f\"`fastexcel` >= 0.10 is required to read bytes; found {module_version})\"\n            raise ModuleUpgradeRequiredError(msg)\n\n        if reading_bytesio:\n            source = source.getvalue()  # type: ignore[union-attr]\n        elif isinstance(source, (BufferedReader, TextIOWrapper)):\n            if \"b\" not in source.mode:\n                msg = f\"file {source.name!r} must be opened in binary mode\"\n                raise OSError(msg)\n            elif (filename := source.name) and Path(filename).exists():\n                source = filename\n            else:\n                source = source.read()\n\n        parser = fastexcel.read_excel(source, **engine_options)\n        sheets = [\n            {\"index\": i + 1, \"name\": nm} for i, nm in enumerate(parser.sheet_names)\n        ]\n        return _read_spreadsheet_calamine, parser, sheets\n\n    msg = f\"unrecognized engine: {engine!r}\"\n    raise NotImplementedError(msg)\n\n\ndef _csv_buffer_to_frame(\n    csv: StringIO,\n    *,","sourceCodeStart":866,"sourceCodeEnd":902,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/io/spreadsheet/functions.py#L866-L902","documentation":"Raised by pl.read_excel (engine='calamine', the default) in _initialise_spreadsheet_parser as an OSError when source is a file handle (BufferedReader or TextIOWrapper) whose mode lacks 'b' — i.e. a file opened in text mode. fastexcel needs bytes or a filename, so polars validates the handle's mode before handing it over.","triggerScenarios":"with open('f.xlsx') as f: pl.read_excel(f) — Python's open() defaults to text mode ('r'), producing a TextIOWrapper that fails the 'b' in mode check. Also open(path, 'rt'), or handles obtained from text-oriented helpers.","commonSituations":"Code copied from csv workflows (text mode is fine there); refactoring from open(path) context managers; passing a handle returned by a generic 'download to file' utility that opened it for text.","solutions":["Open the file in binary mode: open(path, 'rb')","Simplest: pass the path string itself and let polars open the file","If the handle comes from elsewhere, read it as bytes (fh.read() in binary) or use its .name if it points at a real file"],"exampleFix":"# before\nwith open('report.xlsx') as f:  # text mode -> OSError\n    df = pl.read_excel(f)\n\n# after\nwith open('report.xlsx', 'rb') as f:\n    df = pl.read_excel(f)\n# or simply: df = pl.read_excel('report.xlsx')","handlingStrategy":"validation","validationCode":"def ensure_binary_handle(fh):\n    mode = getattr(fh, 'mode', 'b')\n    if 'b' not in mode:\n        fh.close()\n        return open(fh.name, 'rb')\n    return fh\n\nwith ensure_binary_handle(open('report.xlsx')) as fh:\n    df = pl.read_excel(fh, engine='calamine')","typeGuard":"def is_binary_fileobj(fh) -> bool:\n    return hasattr(fh, 'mode') and 'b' in fh.mode","tryCatchPattern":"try:\n    df = pl.read_excel(fh, engine='calamine')\nexcept OSError as e:\n    if 'binary mode' in str(e):\n        df = pl.read_excel(fh.name, engine='calamine')  # pass the path instead\n    else:\n        raise","preventionTips":["Always open workbooks with open(path, 'rb') — or pass the path string and skip handle management","Audit helpers that vend file handles for text defaults","This check is calamine-only; if you rely on text handles elsewhere, isolate the calamine call"],"tags":["polars","excel","calamine","file-mode","io","binary"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}