pydantic/monty · error

TypeBody is decode-only

Error message

TypeBody is decode-only

What it means

`TypeBody` (the decoded wire form of nested type-annotation objects: name, id, origin, attrs) is decode-only in `monty-proto`; its `encode_raw` traps with `unreachable!("TypeBody is decode-only")`. Type bodies are merged field-by-field during decoding (including `PairList` attrs) and converted into `MontyObject`s, never serialized back out. Firing means a decode-only aggregate was fed into an encoder — an internal `monty-proto` bug.

Source

Thrown at crates/monty-proto/src/wire.rs:964

        &mut self,
        tag: u32,
        wire_type: WireType,
        buf: &mut impl Buf,
        ctx: DecodeContext,
    ) -> Result<(), DecodeError> {
        // Field numbers from `Type` in monty.proto; unknown → skip.
        match tag {
            1 => encoding::string::merge(wire_type, &mut self.name, buf, ctx),
            2 => encoding::message::merge(wire_type, self.id.get_or_insert_with(pb::Uuid::default), buf, ctx),
            3 => encoding::int32::merge(wire_type, &mut self.origin, buf, ctx),
            4 => encoding::bool::merge(wire_type, &mut self.is_dataclass, buf, ctx),
            5 => 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!("TypeBody is decode-only")
    }

    fn encoded_len(&self) -> usize {
        unreachable!("TypeBody is decode-only")
    }

    fn clear(&mut self) {
        self.name.clear();
        self.id = None;
        self.origin = 0;
        self.is_dataclass = false;
        self.attrs = None;
    }
}

/// Decode-only `prost::Message` for `NamedTuple`, materializing the
/// `repeated MontyObject values` field straight into `Vec<MontyObject>` (the
/// [`ObjectList`] trick inlined alongside the other two fields) instead of the

View on GitHub (pinned to adc986b362)

Solutions

  1. Encode type-annotation data from `WireObject`/`MontyObject`, never from the decoded `TypeBody`
  2. Implement real encoding for `TypeBody` if bidirectional use is required
  3. Audit message field types so `TypeBody` exists only in decode-side structures
  4. Run `cargo test -p monty-proto` differential tests and `make check-proto`

Example fix

// before
type_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 TypeBody back into MontyObject before any further processing/encoding
let obj: MontyObject = type_body.into();

Type guard

fn is_decode_only_aggregate(msg: &dyn std::any::Any) -> bool { msg.is::<TypeBody>() }

Try / catch

// Avoid statically; encode only WireObject values, never TypeBody

Prevention

When it happens

Trigger: Calling any prost encode routine on a `TypeBody` value, or placing `TypeBody` in a message field that is serialized from parent to child.

Common situations: Encountered while evolving the wire schema (e.g. extending type-annotation representation) or in differential tests that try to re-encode decoded nested structures.

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


AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13). Data as JSON: /api/errors/42002377b053ff31. Report an issue: GitHub.