pydantic/monty · error
PairList is decode-only
Error message
PairList is decode-only
What it means
`PairList` (used to decode recursive lists of key/value pairs, e.g. dict and attribute items in nested MontyObject values) is decode-only in `monty-proto`'s hand-written wire layer; its `encode_raw` traps with `unreachable!("PairList is decode-only")`. Pair lists are produced by validation-while-decoding and converted directly into `MontyObject` structures, so they are never re-encoded. Firing indicates `monty-proto` internal misuse of a decode-only type.
Source
Thrown at crates/monty-proto/src/wire.rs:919
impl Message for PairList {
fn merge_field(
&mut self,
tag: u32,
wire_type: WireType,
buf: &mut impl Buf,
ctx: DecodeContext,
) -> Result<(), DecodeError> {
// `Dict.pairs` is field 1; any other tag is unknown → skip.
if tag == 1 {
merge_pair_item(wire_type, buf, ctx, &mut self.0)
} else {
skip_field(wire_type, tag, buf, ctx)
}
}
fn encode_raw(&self, _buf: &mut impl BufMut) {
unreachable!("PairList is decode-only")
}
fn encoded_len(&self) -> usize {
unreachable!("PairList is decode-only")
}
fn clear(&mut self) {
self.0.clear();
}
}
/// Decode-only `prost::Message` for a wire `Type`, materializing `attrs`
/// straight into a [`PairList`] (each value charged as it decodes) instead of
/// the `Vec<pb::Pair>` the generated `pb::Type` would force. Decode-only;
/// types encode via [`monty_type_to_pb`].
#[derive(Default)]
struct TypeBody {
name: String,View on GitHub (pinned to adc986b362)
Solutions
- Encode pair data from the owning `MontyObject`/`WireObject`, never from the decoded `PairList`
- Implement real encoding in `PairList` if bidirectional use is ever required
- Ensure no serialized message field holds a `PairList` value on the encode side
- Run `cargo test -p monty-proto` (differential oracle tests) to validate
Example fix
// before pair_list.encode_to_vec() // panics: decode-only // after WireObject::from(&monty_object).encode_to_vec()
Defensive patterns
Strategy: type-guard
Validate before calling
// Encode pair data via the owning object, never the decoded list let bytes = WireObject::from(&monty_object).encode_to_vec();
Type guard
fn is_decode_only_aggregate(msg: &dyn std::any::Any) -> bool { msg.is::<PairList>() } Try / catch
// Avoid statically; convert decoded aggregates back to MontyObject/WireObject before encoding
Prevention
- Treat `PairList` as a decoding intermediate only — convert immediately to `MontyObject`
- Never store `PairList` in encoded message structs
- Document the decode-only contract on the type
- Run `cargo test -p monty-proto` differential tests after wire changes
When it happens
Trigger: Passing a `PairList` to any prost encoding routine (`encode_to_vec`, `encode_raw` invocation, or embedding it in a serialized message field).
Common situations: Hit during wire-protocol development: round-tripping decoded values in tests, or refactoring the encoder to share aggregate types between directions.
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
- TypeBody is decode-only
- NamedTupleBody 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/de803b86ecd75de3.
Report an issue: GitHub.