{"record":{"id":"f38f0de1fa8db254","repo":"pydantic/monty","slug":"namedtuplebody-is-decode-only","errorCode":null,"errorMessage":"NamedTupleBody is decode-only","messagePattern":"NamedTupleBody is decode-only","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/monty-proto/src/wire.rs","lineNumber":1010,"sourceCode":"impl Message for NamedTupleBody {\n    fn merge_field(\n        &mut self,\n        tag: u32,\n        wire_type: WireType,\n        buf: &mut impl Buf,\n        ctx: DecodeContext,\n    ) -> Result<(), DecodeError> {\n        // Field numbers from `NamedTuple` in monty.proto; unknown → skip.\n        match tag {\n            1 => encoding::string::merge(wire_type, &mut self.type_name, buf, ctx),\n            2 => encoding::string::merge_repeated(wire_type, &mut self.field_names, buf, ctx),\n            3 => merge_object_item(wire_type, buf, ctx, &mut self.values),\n            _ => skip_field(wire_type, tag, buf, ctx),\n        }\n    }\n\n    fn encode_raw(&self, _buf: &mut impl BufMut) {\n        unreachable!(\"NamedTupleBody is decode-only\")\n    }\n\n    fn encoded_len(&self) -> usize {\n        unreachable!(\"NamedTupleBody is decode-only\")\n    }\n\n    fn clear(&mut self) {\n        self.type_name.clear();\n        self.field_names.clear();\n        self.values.clear();\n    }\n}\n\n/// Decode-only `prost::Message` for `ClassInstance`, decoding the `attrs`\n/// field (a `Dict`) straight into [`DictPairs`] via [`PairList`] rather\n/// than the `Vec<pb::Pair>` wrapper the generated `pb::ClassInstance` would\n/// build and then unwrap. The message fields stay `Option` so an absent one\n/// is rejected by the caller (presence, not a default). Decode-only.","sourceCodeStart":992,"sourceCodeEnd":1028,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty-proto/src/wire.rs#L992-L1028","documentation":"`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`.","triggerScenarios":"Passing a `NamedTupleBody` to prost encoding (`encode_to_vec`, frame-size computation) or embedding it in a serialized message field.","commonSituations":"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.","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"],"exampleFix":"// before\nnamed_tuple_body.encode_to_vec() // panics: decode-only\n// after\nWireObject::from(&monty_object).encode_to_vec()","handlingStrategy":"type-guard","validationCode":"// Convert decoded NamedTupleBody into MontyObject immediately after merge\nlet obj: MontyObject = named_tuple_body.into();","typeGuard":"fn is_decode_only_aggregate(msg: &dyn std::any::Any) -> bool { msg.is::<NamedTupleBody>() }","tryCatchPattern":"// Avoid statically; re-encode from WireObject, not NamedTupleBody","preventionTips":["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"],"tags":["rust","protobuf","wire-protocol","internal-assert"],"backgroundTag":"internal-invariant-violation","analyzedSha":"adc986b362e3961f407868cb118a99fe831b9e61","analyzedAt":"2026-09-13T19:19:18.698Z","contentChangedAt":"2026-09-13T19:19:18.698Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}