# Public workbook exceptions

Unreleased

This exception contract is in source and has not shipped in either edition. Community 2.0.36 and Commercial 2.3.0 can raise the underlying error instead, for example `zipfile.BadZipFile` from `load_workbook` on a file that is not a ZIP package. Until a release carries the contract, catch those errors alongside `InvalidFileException`.

Catch `wolfxl.utils.exceptions.InvalidFileException` for malformed XLSX source content discovered by `load_workbook`/`open`, worksheet reads, `calculate`, `recalculate`, or `save`. Source decoding is lazy: successful loading does not prove that every worksheet can be read or recalculated.

```
from wolfxl import load_workbook
from wolfxl.utils.exceptions import InvalidFileException

try:
    with load_workbook("input.xlsx") as workbook:
        rows = list(workbook.active.values)
        workbook.calculate()
        workbook.save("output.xlsx")
except InvalidFileException as error:
    original_error = error.__cause__
```

## Error families

| Public operation | Malformed-source failures | Other exceptions callers should handle |
| --- | --- | --- |
| `load_workbook`, `open` | `InvalidFileException` for unrecognized input and XLSX ZIP/XML decoding failures, including corrupt DEFLATE and unsupported ZIP compression | `TypeError` for invalid source types; `ValueError` for invalid options or passwords; `NotImplementedError` for unsupported format/mode combinations; `RequiresWolfXLProError` for features requiring the add-on; `NumbersImportError` for Numbers decoding refusals |
| Cell reads, row/column iteration, dimensions, value chunks | `InvalidFileException` for native source XML, reference and shared-string decoding failures | `ValueError` or `CellCoordinatesException` for invalid caller coordinates, ranges and chunk sizes; `KeyError` for an unknown requested sheet; `WorkbookClosedException` after closing |
| `calculate`, `recalculate` | `InvalidFileException` for source decoding during formula intake or cell hydration | `ValueError` for invalid calculation options or perturbations; `ReadOnlyWorkbookException` for committed recalculation on read-only workbooks; documented calculation errors such as `UnsupportedRustFormula`, `FormulaErrorResultError` and `RustCalcBindingError` for formula or backend refusals |
| `save` | `InvalidFileException` for source decoding during formula-cache collection, source promotion or worksheet flushing; invalid non-spreadsheet declarations for the workbook part are refused during promotion | `ValueError`, `NotImplementedError`, `ReadOnlyWorkbookException`, `WorkbookClosedException` and documented save-policy errors for invalid requests or unsupported operations; filesystem/output-stream errors for destination failures |

Filesystem failures with an OS error number retain their original exception type. `InvalidFileException` also inherits from `OSError` and remains compatible with openpyxl's exception of the same name. A non-ZIP OOXML input can additionally be caught as `zipfile.BadZipFile`.

When wrapping a decoder or source-validation error, WolfXL retains the original exception in `__cause__`. Already typed file errors retain their existing cause rather than receiving another wrapper. Format-identification refusals without an underlying decoder exception do not fabricate a cause. Python warning callbacks, custom formula callbacks, cancellation and feature-specific policy refusals retain their own documented contracts.

The contract normalizes source-decoder failures when they occur. It does not require eager validation by every operation. Native streaming can filter out-of-grid cells, and calculation can leave unrelated source scalars unread.

A macro-enabled spreadsheet workbook declaration is valid. A VBA-project or Word document declaration for the spreadsheet workbook part is a different case.

## Reader and package refusals

Catch `InvalidFileException` for an out-of-range shared-string index, for nonascending or repeated rows refused by a read-only reader, and for unsafe ZIP metadata or package-size admission failures. The native refusal message and recovery advice remain available; native admission refusals also retain their `INVALID_SOURCE` diagnostic code. Explicit `Limits` budget failures remain `LimitExceeded`, and invalid caller options remain `ValueError`.

## Reproducible coverage

`tests/test_public_exception_contract.py` contains a deterministic, standalone package generator and the 27 malformed cases: ten deeply nested unknown-element positions, an unclosed row, six invalid cell references, three shared-string-table failures, an empty sheet title, three damaged DEFLATE members, unsupported ZIP compression, and two non-spreadsheet workbook-part declarations. The same tests exercise deferred read/calculation/save failures, direct and streaming reads, valid path/bytes/stream inputs, and macro-enabled workbook saves. No external corpus is needed.
