pydantic/monty · error
ClassInstanceBody is decode-only
Error message
ClassInstanceBody is decode-only
What it means
`ClassInstanceBody` is another decode-only hand-written prost `Message` impl in wire.rs: its `encode_raw` panics with `unreachable!("ClassInstanceBody is decode-only")`. The struct exists purely to decode class-instance values from the wire; it is never meant to be serialized. Hitting the panic means an encode path reached a decode-only type.
Source
Thrown at crates/monty-proto/src/wire.rs:1066
1 => encoding::message::merge(
wire_type,
self.class_type.get_or_insert_with(TypeBody::default),
buf,
ctx,
),
2 => encoding::message::merge(
wire_type,
self.instance_id.get_or_insert_with(pb::Uuid::default),
buf,
ctx,
),
3 => encoding::message::merge(wire_type, self.attrs.get_or_insert_with(PairList::default), buf, ctx),
_ => skip_field(wire_type, tag, buf, ctx),
}
}
fn encode_raw(&self, _buf: &mut impl BufMut) {
unreachable!("ClassInstanceBody is decode-only")
}
fn encoded_len(&self) -> usize {
unreachable!("ClassInstanceBody is decode-only")
}
fn clear(&mut self) {
self.class_type = None;
self.instance_id = None;
self.attrs = None;
}
}
/// Maps a semantic validation failure onto prost's decode error so it
/// surfaces through the normal frame-decode path.
//
// `DecodeError::new` is deprecated but has no public replacement in prost
// 0.14 (`DecodeErrorKind` is crate-private); the deprecation note itselfView on GitHub (pinned to adc986b362)
Solutions
- Encode class-instance values through the designated encode representation, never through `ClassInstanceBody`.
- If class instances must be encoded, implement real `encode_raw`/`encoded_len` (fields: class_type, instance_id, attrs PairList) or regenerate a prost message for both directions.
- Run `make check-proto` to confirm the wire schema and hand-written impls stayed in sync.
- Replace the value with an encodable representation before sending.
Example fix
// before
fn encode_raw(&self, _buf: &mut impl BufMut) { unreachable!("ClassInstanceBody is decode-only") }
// after
fn encode_raw(&self, buf: &mut impl BufMut) {
if let Some(t) = &self.class_type { encoding::message::encode(...); }
// ...
} Defensive patterns
Strategy: validation
Validate before calling
// Rust: ensure class instances are encoded via the supported representation assert!(encodable(value), "ClassInstanceBody is decode-only; use the encode path for class instances");
Prevention
- Treat ClassInstanceBody strictly as a decode target; encode class instances with the mirrored generated message.
- Add encode/decode round-trip tests for class-instance values.
- Run make check-proto in CI before merging wire changes.
When it happens
Trigger: Serializing a MontyObject representing a class instance (with `class_type`, `instance_id`, `attrs`) through the hand-written `ClassInstanceBody` Message impl — e.g. a worker trying to send a class-instance value back to the parent via this body type.
Common situations: Extending the wire protocol to round-trip new object kinds and accidentally routing class instances into the decode-only struct on the encode side; refactoring `WireObject` conversions so encode and decode no longer use mirrored types.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- ObjectList is decode-only
- PairList is decode-only
- TypeBody is decode-only
- NamedTupleBody is decode-only
- task coroutine_id doesn't point to a Coroutine
AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13).
Data as JSON: /api/errors/055d1edd43a443d3.
Report an issue: GitHub.