Errors across languages
The .NET library throws exceptions. The C ABI turns each failure into an XL_* status code, and the
Python, Rust and C++ bindings turn that code into their own error type. This page maps one onto the
other, so a failure seen in one language can be recognized in another.
Status codes
Every xl_* function returns one of these. After a failure, xl_last_error_ptr returns the message
for the calling thread.
| Code | Constant | Meaning |
|---|---|---|
| 0 | XL_OK |
Success. |
| -1 | XL_EOF |
No more rows or batches. Not an error: the bindings end iteration on it. |
| -2 | XL_BUFFER_TOO_SMALL |
The caller's buffer cannot hold the result. The bindings grow the buffer and retry, so it never reaches user code. |
| -3 | XL_INVALID_HANDLE |
The handle is null, already closed, or was never opened. |
| -4 | XL_INVALID_ARGUMENT |
An argument failed validation before any work started: a null pointer, a bad length, malformed options. |
| -5 | XL_ERROR |
Anything else. The message is the message of the .NET exception behind it. |
| -6 | XL_STATUS_PASSWORD_REQUIRED |
The workbook is encrypted and no password was supplied. |
| -7 | XL_STATUS_PASSWORD_INCORRECT |
The supplied password did not match the workbook's verifier. |
By failure
| Failure | C# | Status | Python | Rust | C++ |
|---|---|---|---|---|---|
| Encrypted, no password | ExcelEncryptionException, Reason == PasswordRequired |
-6 | PasswordRequiredError |
Error::PasswordRequired |
xl::Error, code == XL_STATUS_PASSWORD_REQUIRED |
| Wrong password | ExcelEncryptionException, Reason == PasswordIncorrect |
-7 | PasswordIncorrectError |
Error::PasswordIncorrect |
xl::Error, code == XL_STATUS_PASSWORD_INCORRECT |
| Unsupported encryption, or failed integrity check | ExcelEncryptionException, Reason is UnsupportedScheme or IntegrityFailure |
-5 | ExcelReaderError |
Error::Native |
xl::Error, code == XL_ERROR |
| Corrupt or malformed file | InvalidDataException |
-5 | ExcelReaderError |
Error::Native |
xl::Error, code == XL_ERROR |
| Feature the reader does not support | NotSupportedException |
-5 | ExcelReaderError |
Error::Native |
xl::Error, code == XL_ERROR |
| File exceeds a configured resource limit | ExcelLimitExceededException |
-5 | ExcelReaderError |
Error::Native |
xl::Error, code == XL_ERROR |
| Workbook already closed | ObjectDisposedException |
-3 | ExcelReaderError |
Error::Native |
xl::Error, code == XL_INVALID_HANDLE |
| Invalid argument | ArgumentException and its subclasses |
-4, or -5 when the check happens past the ABI boundary | ExcelReaderError |
Error::Native |
xl::Error |
PasswordRequiredError and PasswordIncorrectError derive from ExcelReaderError, so
except ExcelReaderError catches every native failure in Python. Rust's Error::code() returns the
status for every variant. The C++ header throws nothing: every entry point returns
std::expected<T, xl::Error>.
Only the two password cases are worth retrying with different input. The rest fail the same way on every attempt.
Each binding also checks some arguments itself, before calling the ABI, and reports those with its
own native types: Python raises ValueError, TypeError or IndexError; Rust panics on an
out-of-range index (see the crate README's "Bounds and panics"); C++ returns xl::Error with
code == XL_INVALID_ARGUMENT.