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 deserialization

CrousError

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 errors

CrousEncodeError

Raised when serialization fails. Common causes:

  • Unsupported type — object type is not supported and no default handler 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:

CodeNameDescriptionCritical
0CROUS_OKSuccess
1CROUS_ERR_OOMOut of memory
2CROUS_ERR_OVERFLOWBuffer/integer overflow
3CROUS_ERR_TYPEUnknown/unsupported type
4CROUS_ERR_INVALID_DATAMalformed or corrupt data
5CROUS_ERR_IOI/O failure
6CROUS_ERR_DEPTHMax nesting depth exceeded
7CROUS_ERR_SIZESize limit exceeded
8CROUS_ERR_UTF8Invalid UTF-8 encoding
9CROUS_ERR_VERSIONIncompatible version
10CROUS_ERR_UNSUPPORTEDUnsupported operation
11CROUS_ERR_INTERNALInternal error
12CROUS_ERR_PARSEParse 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}")
        raise

Validating 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}")