pydantic/monty · error
NamedTupleBody is decode-only
Error message
NamedTupleBody is decode-only
What it means
`NamedTupleBody` (decoded wire form of named-tuple fields/values inside nested `MontyObject`s) follows the same decode-only pattern in `crates/monty-proto/src/wire.rs`: `encode_raw` and `encoded_len` trap with `unreachable!("NamedTupleBody is decode-only")`. Named tuples are merged during decoding (`merge_object_item` into `self.values`) and converted to `MontyObject`s, never re-serialized. Firing signals internal misuse of a decode-only aggregate in `monty-proto`.
Source
Thrown at crates/monty-proto/src/wire.rs:1010
impl Message for NamedTupleBody {
fn merge_field(
&mut self,
tag: u32,
wire_type: WireType,
buf: &mut impl Buf,
ctx: DecodeContext,
) -> Result<(), DecodeError> {
// Field numbers from `NamedTuple` in monty.proto; unknown → skip.
match tag {
1 => encoding::string::merge(wire_type, &mut self.type_name, buf, ctx),
2 => encoding::string::merge_repeated(wire_type, &mut self.field_names, buf, ctx),
3 => merge_object_item(wire_type, buf, ctx, &mut self.values),
_ => skip_field(wire_type, tag, buf, ctx),
}
}
fn encode_raw(&self, _buf: &mut impl BufMut) {
unreachable!("NamedTupleBody is decode-only")
}
fn encoded_len(&self) -> usize {
unreachable!("NamedTupleBody is decode-only")
}
fn clear(&mut self) {
self.type_name.clear();
self.field_names.clear();
self.values.clear();
}
}
/// Decode-only `prost::Message` for `ClassInstance`, decoding the `attrs`
/// field (a `Dict`) straight into [`DictPairs`] via [`PairList`] rather
/// than the `Vec<pb::Pair>` wrapper the generated `pb::ClassInstance` would
/// build and then unwrap. The message fields stay `Option` so an absent one
/// is rejected by the caller (presence, not a default). Decode-only.View on GitHub (pinned to adc986b362)
Solutions
- Re-encode from the originating `MontyObject`/`WireObject`, never from the decoded `NamedTupleBody`
- Implement genuine `encode_raw`/`encoded_len` if bidirectional serialization is ever needed
- Ensure no encode-side message struct contains a `NamedTupleBody` field
- Run `make check-proto` and `cargo test -p monty-proto` differential tests
Example fix
// before named_tuple_body.encode_to_vec() // panics: decode-only // after WireObject::from(&monty_object).encode_to_vec()
Defensive patterns
Strategy: type-guard
Validate before calling
// Convert decoded NamedTupleBody into MontyObject immediately after merge let obj: MontyObject = named_tuple_body.into();
Type guard
fn is_decode_only_aggregate(msg: &dyn std::any::Any) -> bool { msg.is::<NamedTupleBody>() } Try / catch
// Avoid statically; re-encode from WireObject, not NamedTupleBody
Prevention
- Treat `NamedTupleBody` as a decode intermediate; convert to `MontyObject` right away
- Keep it out of serialized message fields
- Document the decode-only contract on the type
- Run `make check-proto` and `cargo test -p monty-proto` after wire-layer changes
When it happens
Trigger: Passing a `NamedTupleBody` to prost encoding (`encode_to_vec`, frame-size computation) or embedding it in a serialized message field.
Common situations: Seen while extending wire value representation (e.g. adding named-tuple support to more message kinds) or in differential tests that re-encode decoded nested values.
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
- varargs/varkwargs own no param slot
- checked above
AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13).
Data as JSON: /api/errors/f38f0de1fa8db254.
Report an issue: GitHub.