Error Handling
Crous uses a hierarchical exception system. All Crous exceptions inherit fromCrousError, with specialized subclasses for encoding and decoding errors.
Exception Hierarchy
Exception
└── CrousError # Base exception for all Crous errors
├── CrousEncodeError # Raised during serialization
└── CrousDecodeError # Raised during deserializationCrousError
The base exception class. Catch this to handle all Crous-related errors.
catch_all.py
import crous
try:
result = crous.loads(b"invalid data")
except crous.CrousError as e:
print(f"Crous error: {e}") # catches both encode and decode errorsCrousEncodeError
Raised when serialization fails. Common causes:
- Unsupported type — object type is not supported and no
defaulthandler is provided - Non-string dict keys — dictionaries with integer, tuple, or other non-string keys
- Integer overflow — integer value exceeds 64-bit signed range
- Nesting too deep — structure nesting exceeds 256 levels
- Size limit — string or bytes exceeds 64 MB
encode_errors.py
import crous
# Unsupported type
try:
crous.dumps(object())
except crous.CrousEncodeError as e:
print(f"Encode error: {e}")
# Non-string dict keys
try:
crous.dumps({1: "value"})
except crous.CrousEncodeError as e:
print(f"Key error: {e}")
# Integer overflow
try:
crous.dumps(2**64)
except (crous.CrousEncodeError, OverflowError) as e:
print(f"Overflow: {e}")CrousDecodeError
Raised when deserialization fails. Common causes:
- Truncated data — binary data is incomplete
- Invalid magic bytes — data doesn't start with valid format header
- Corrupted data — bytes are malformed or tampered with
- Invalid UTF-8 — string data contains invalid UTF-8 sequences
- Version mismatch — wire format version is unsupported
decode_errors.py
import crous
# Truncated data
try:
crous.loads(b"FLUX\x01") # incomplete
except crous.CrousDecodeError as e:
print(f"Decode error: {e}")
# Invalid data
try:
crous.loads(b"not crous data")
except crous.CrousDecodeError as e:
print(f"Invalid: {e}")
# File not found
try:
crous.load("nonexistent.crous")
except (crous.CrousDecodeError, FileNotFoundError) as e:
print(f"File error: {e}")C-Level Error Codes
Internally, the C library uses numeric error codes that are mapped to Python exceptions:
| Code | Name | Description | Critical |
|---|---|---|---|
| 0 | CROUS_OK | Success | — |
| 1 | CROUS_ERR_OOM | Out of memory | ✅ |
| 2 | CROUS_ERR_OVERFLOW | Buffer/integer overflow | ✅ |
| 3 | CROUS_ERR_TYPE | Unknown/unsupported type | |
| 4 | CROUS_ERR_INVALID_DATA | Malformed or corrupt data | |
| 5 | CROUS_ERR_IO | I/O failure | |
| 6 | CROUS_ERR_DEPTH | Max nesting depth exceeded | |
| 7 | CROUS_ERR_SIZE | Size limit exceeded | |
| 8 | CROUS_ERR_UTF8 | Invalid UTF-8 encoding | |
| 9 | CROUS_ERR_VERSION | Incompatible version | |
| 10 | CROUS_ERR_UNSUPPORTED | Unsupported operation | |
| 11 | CROUS_ERR_INTERNAL | Internal error | ✅ |
| 12 | CROUS_ERR_PARSE | Parse error |
Critical Errors
Error codes marked as "Critical" (OOM, OVERFLOW, INTERNAL) indicate severe failures that may leave the library in an inconsistent state. These should be treated as unrecoverable.
Best Practices
best_practices.py
import crous
def safe_serialize(data, filepath=None):
"""Serialize data with comprehensive error handling."""
try:
if filepath:
crous.dump(data, filepath)
else:
return crous.dumps(data)
except crous.CrousEncodeError as e:
# Handle serialization-specific errors
print(f"Cannot serialize: {e}")
raise
except crous.CrousError as e:
# Handle any other Crous error
print(f"Crous error: {e}")
raise
def safe_deserialize(source):
"""Deserialize data with comprehensive error handling."""
try:
if isinstance(source, (str, bytes)) and not isinstance(source, bytes):
return crous.load(source)
else:
return crous.loads(source)
except crous.CrousDecodeError as e:
print(f"Cannot deserialize: {e}")
raise
except FileNotFoundError:
print(f"File not found: {source}")
raise
except crous.CrousError as e:
print(f"Crous error: {e}")
raiseValidating Data
Use the version module to validate binary data before decoding:
validation.py
from crous.version import check_compatibility, Header
binary_data = b"FLUX\x01\x00..."
# Parse and validate header
header = Header.parse(binary_data)
if header.is_valid:
print(f"Valid FLUX data, version {header.wire_version}")
# Check compatibility
result = check_compatibility(binary_data)
if result.is_compatible:
data = crous.loads(binary_data)
elif result.is_warning:
print(f"Warning: {result.message}")
data = crous.loads(binary_data) # try anyway
else:
print(f"Incompatible: {result.message}")