5. Inspect an error¶
This continues the frame defined in 2. Define and pack a structure. All
public errors derive from rustruct.RustructError:
rustruct.SchemaErrormeans the declaration is inconsistent.rustruct.PackErrormeans Python values cannot satisfy the schema.rustruct.InvalidDataErrormeans input bytes violate the schema.
Pack and decode errors expose machine-readable details:
from rustruct import InvalidDataError, Struct, U8, U16, U32, digest, slice
class ChatFrame(Struct, byteorder="network"):
kind: U8
seq: U16
body_length: U16
body: bytes = slice(len="body_length")
crc: U32 = digest("crc32", over="*")
try:
ChatFrame.unpack(b"\x01")
except InvalidDataError as exc:
print(exc.kind, exc.path, exc.offset)
Nested errors carry a dotted field path; array failures include the item
location where applicable. A digest mismatch is its own
rustruct.InvalidDataError.kind, distinct from a truncated or
malformed buffer. A single bit flipped in transit – the kind of damage a
noisy link can cause – unpacks as a checksum failure, not silent garbage:
wire = ChatFrame(kind=2, seq=1, body=b"hello").pack()
corrupted = wire[:-1] + bytes([wire[-1] ^ 0xFF]) # flip the crc's last byte
try:
ChatFrame.unpack(corrupted)
except InvalidDataError as exc:
assert exc.kind == "checksum"
assert exc.path == "crc"
assert exc.offset == 10
Previous: 4. Try partial input.
You now have the basic pack, unpack, and incremental-parse workflow, and a
frame format that could actually run over a socket. Continue with
How to model tagged unions to give kind real per-type payloads
(join, text, ping), or read How schemas work to understand
why lengths and bounded regions behave this way.