{"id":"a7b43e5b74345f09","repo":"serde-rs/serde","slug":"mapaccess-next-value-called-before-next-key-a7b43e","errorCode":null,"errorMessage":"MapAccess::next_value called before next_key","messagePattern":"MapAccess::next_value called before next_key","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"serde_core/src/de/value.rs","lineNumber":1383,"sourceCode":"        T: de::DeserializeSeed<'de>,\n    {\n        match self.next_pair() {\n            Some((key, value)) => {\n                self.value = Some(value);\n                seed.deserialize(key.into_deserializer()).map(Some)\n            }\n            None => Ok(None),\n        }\n    }\n\n    fn next_value_seed<T>(&mut self, seed: T) -> Result<T::Value, Self::Error>\n    where\n        T: de::DeserializeSeed<'de>,\n    {\n        let value = self.value.take();\n        // Panic because this indicates a bug in the program rather than an\n        // expected failure.\n        let value = value.expect(\"MapAccess::next_value called before next_key\");\n        seed.deserialize(value.into_deserializer())\n    }\n\n    fn next_entry_seed<TK, TV>(\n        &mut self,\n        kseed: TK,\n        vseed: TV,\n    ) -> Result<Option<(TK::Value, TV::Value)>, Self::Error>\n    where\n        TK: de::DeserializeSeed<'de>,\n        TV: de::DeserializeSeed<'de>,\n    {\n        match self.next_pair() {\n            Some((key, value)) => {\n                let key = tri!(kseed.deserialize(key.into_deserializer()));\n                let value = tri!(vseed.deserialize(value.into_deserializer()));\n                Ok(Some((key, value)))\n            }","sourceCodeStart":1365,"sourceCodeEnd":1401,"githubUrl":"https://github.com/serde-rs/serde/blob/747814f7d5fbab872df3b02f070c165b91bde062/serde_core/src/de/value.rs#L1365-L1401","documentation":"This is a panic (via .expect at serde_core/src/de/value.rs:1383) in the PUBLIC serde_core::de::value::MapDeserializer<I,E>, the generic adapter that turns any Iterator of (K,V) pairs into a Deserializer. The struct (defined at value.rs:1227) holds 'value: Option<Second<I::Item>>' staged by next_key_seed->next_pair and consumed by next_value_seed; calling next_value_seed when it is None means the consuming Visitor/Deserialize broke the MapAccess protocol (next_value before a successful next_key). Because this MapDeserializer is part of the public API, this is the variant application code most often constructs directly via MapDeserializer::new(iter).","triggerScenarios":"Constructing MapDeserializer::new(map.into_iter()) (or via IntoDeserializer on a HashMap/BTreeMap) to deserialize into a type whose Visitor::visit_map calls map.next_value()? without a preceding map.next_key()?; or calls next_value twice per key; or calls next_value after next_key returned None. Common in unit tests that feed a custom MapDeserializer to exercise a Deserialize impl, and in glue code that turns a runtime map into a Deserializer for a config/schema type.","commonSituations":"Tests for a custom Deserialize that build a MapDeserializer from a vec of tuples; config parsers that turn a HashMap into a Deserializer via IntoDeserializer; converting between map representations (toml/json/HashMap) into a typed struct whose Deserialize is hand-written and buggy; bumping serde_core version where this MapDeserializer moved out of serde into serde_core (1.0.x split), changing the panic file/line but not the cause.","solutions":["Fix the consuming Visitor::visit_map so every map.next_value()? is inside 'while let Some(k) = map.next_key()? { .. }' (or 'if let Some(k)') with exactly one next_value per key.","Drive the map with map.next_entry()? / next_entry_seed returning Option<(K,V)>, which is atomic and cannot trigger this panic.","If the MapDeserializer is constructed in a test, assert map.next_key() returns Some before asserting on next_value; restructure the test to mirror the derived visit_map loop.","Replace the hand-written Deserialize with #[derive(Deserialize)] to eliminate the protocol-violation surface entirely.","Confirm you depend on serde_core (not just serde) and that versions of serde and serde_core match; a skew between the two can route through an unexpected MapDeserializer impl."],"exampleFix":"// before — test feeds MapDeserializer to a visit_map that calls next_value first\nlet md = MapDeserializer::new(vec![(\"k\", 1)].into_iter());\nlet v: Foo = Deserialize::deserialize(md)?;\n// inside Foo's visit_map:\n//   let v: i32 = map.next_value()?;  // PANIC at value.rs:1383\n\n// after — visit_map pairs key+value (or uses next_entry)\nfn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>\nwhere A: serde::de::MapAccess<'de>,\n{\n    let mut n = 0;\n    while let Some((k, v)): (String, i32) = map.next_entry()? {\n        if k == \"k\" { n = v; }\n    }\n    Ok(Foo(n))\n}","handlingStrategy":"validation","validationCode":"// Validate the consumer BEFORE wiring a public MapDeserializer into it:\n// run this against the target Deserialize to confirm it honors the protocol.\n// If this panics, the bug is in MyType's Visitor, not in MapDeserializer.\n#[cfg(test)]\nfn assert_map_protocol_honored() {\n    use serde::Deserialize;\n    use serde_core::de::value::MapDeserializer;\n    let md = MapDeserializer::new(vec![(\"k\".to_string(), 1i64)].into_iter());\n    // should be Ok; a panic here == next_value-before-next_key in MyType\n    let _v: MyType = MyType::deserialize(md).expect(\"protocol honored\");\n}\n// In real visit_map code, use next_entry to make the panic impossible:\n//   while let Some((k, v)): (String, i32) = map.next_entry()? { .. }","typeGuard":null,"tryCatchPattern":"// Last-resort safety net around a MapDeserializer-driven deserialize path.\n// Fix the offending Visitor; this only prevents the panic from killing the process.\nuse std::panic::{catch_unwind, AssertUnwindSafe};\nuse serde_core::de::value::MapDeserializer;\nlet md = MapDeserializer::new(pairs.into_iter());\nlet res = catch_unwind(AssertUnwindSafe(|| {\n    MyType::deserialize(md)\n}));\nmatch res {\n    Ok(Ok(v)) => Ok(v),\n    Ok(Err(e)) => Err(e.into()),\n    Err(_) => Err(\"serde_core MapAccess protocol violation: next_value before next_key\".into()),\n}","preventionTips":["When feeding a public MapDeserializer to a Deserialize, drive the consumer's visit_map with next_entry() / next_entry_seed() — atomic, panic-proof.","Keep serde and serde_core versions in lockstep (serde 1.0.x re-exports from serde_core); a version skew can route through an unexpected MapDeserializer impl.","In tests, build MapDeserializer from a small known vec of tuples and assert next_key() returns Some before asserting on any next_value().","Prefer #[derive(Deserialize)] for the target type; hand-written Visitors are the only realistic source of this panic.","Grep your tests and glue code for 'MapDeserializer::new' and confirm each consumer type's visit_map pairs key+value correctly."],"tags":["serde","serde-core","rust","deserialization","mapaccess","panic","into-deserializer","public-api"],"analyzedSha":"747814f7d5fbab872df3b02f070c165b91bde062","analyzedAt":"2026-08-06T01:20:43.063Z","schemaVersion":2}