{"record":{"id":"78eb27cd737d2453","repo":"serde-rs/json","slug":"serialize-value-called-before-serialize-key","errorCode":null,"errorMessage":"serialize_value called before serialize_key","messagePattern":"serialize_value called before serialize_key","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/value/ser.rs","lineNumber":426,"sourceCode":"                Ok(())\n            }\n            #[cfg(feature = \"arbitrary_precision\")]\n            SerializeMap::Number { .. } => unreachable!(),\n            #[cfg(feature = \"raw_value\")]\n            SerializeMap::RawValue { .. } => unreachable!(),\n        }\n    }\n\n    fn serialize_value<T>(&mut self, value: &T) -> Result<()>\n    where\n        T: ?Sized + Serialize,\n    {\n        match self {\n            SerializeMap::Map { map, next_key } => {\n                let key = next_key.take();\n                // Panic because this indicates a bug in the program rather than an\n                // expected failure.\n                let key = key.expect(\"serialize_value called before serialize_key\");\n                map.insert(key, tri!(to_value(value)));\n                Ok(())\n            }\n            #[cfg(feature = \"arbitrary_precision\")]\n            SerializeMap::Number { .. } => unreachable!(),\n            #[cfg(feature = \"raw_value\")]\n            SerializeMap::RawValue { .. } => unreachable!(),\n        }\n    }\n\n    fn end(self) -> Result<Value> {\n        match self {\n            SerializeMap::Map { map, .. } => Ok(Value::Object(map)),\n            #[cfg(feature = \"arbitrary_precision\")]\n            SerializeMap::Number { .. } => unreachable!(),\n            #[cfg(feature = \"raw_value\")]\n            SerializeMap::RawValue { .. } => unreachable!(),\n        }","sourceCodeStart":408,"sourceCodeEnd":444,"githubUrl":"https://github.com/serde-rs/json/blob/afdf6fc67247dd7fa4fcde1381e6ecc6bcc7a30e/src/value/ser.rs#L408-L444","documentation":"This panic comes from serde_json's value Serializer (the one backing to_value) at src/value/ser.rs:426, inside SerializeMap::Map::serialize_value: it does next_key.take().expect(\"serialize_value called before serialize_key\"). The serde SerializeMap contract requires each value to be preceded by exactly one serialize_key (or use serialize_entry, which does both). The inline comment at src/value/ser.rs:424-425 states this is treated as a bug in the program, not an expected failure, so it panics rather than returning an error. It can only fire when serializing into a serde_json::Value via this internal serializer.","triggerScenarios":"A hand-written serde::Serialize impl for a map-like type that drives the Serializer directly and calls serialize_value(&v) before any serialize_key(&k) on the same SerializeMap handle, or calls serialize_value twice. Triggered specifically through serde_json::to_value (or the public value::Serializer) on such a type, because the SerializeMap returned by serialize_struct/serialize_map is serde_json's SerializeMap::Map (src/value/ser.rs:264-269, 271-279). A wrapper Serializer/adapter that reorders or drops the serialize_key call before forwarding serialize_value also reproduces it.","commonSituations":"Developers writing a custom Serialize for a HashMap-wrapper, Multimap, or ordered-map type and forgetting the key/value pairing. A serde adapter (e.g. a flattening or renaming wrapper crate) that forwards serialize_value but conditionally skips serialize_key. Code copied from an example that used serialize_entry, later split into separate key/value calls with a bug. Rare with #[derive(Serialize)] — derived code always uses serialize_entry, so this usually points at hand-rolled serialization.","solutions":["Drive the map with serialize_entry(k, v) instead of separate serialize_key/serialize_value calls — it cannot be misordered.","If you must call them separately, guarantee strict serialize_key then serialize_value pairing in a loop and never call serialize_value twice for one key.","Audit any custom Serialize impl or Serializer wrapper that touches serde_json::to_value / value::Serializer for a missing or conditional serialize_key before serialize_value.","Add a unit test that runs to_value(&your_type) over an empty and a populated instance to surface the misordering before runtime.","If you only need a JSON object, build a serde_json::Map/Value directly instead of implementing Serialize."],"exampleFix":"// before — hand-rolled Serialize for a map type, driven via to_value\nlet mut m = serializer.serialize_map(Some(self.len()))?;\nfor (k, v) in self.iter() {\n    m.serialize_value(v)?; // BUG: no preceding serialize_key -> panics\n}\nm.end()\n\n// after — use serialize_entry (or pair serialize_key + serialize_value)\nlet mut m = serializer.serialize_map(Some(self.len()))?;\nfor (k, v) in self.iter() {\n    m.serialize_entry(k, v)?;\n}\nm.end()","handlingStrategy":"validation","validationCode":"// Nothing to call before the API at runtime — the panic is a contract violation\n// inside a Serialize impl driven by serde_json's value Serializer.\n// Validate by round-tripping your type through to_value in a test:\n#[cfg(test)]\nfn check_serialization<T: serde::Serialize>(v: &T) -> Result<serde_json::Value, String> {\n    serde_json::to_value(v).map_err(|e| e.to_string())\n}\n// Call with both empty and populated instances; a misordered key/value pair\n// will panic here in the test rather than in production.","typeGuard":"// Not a type-guardable condition. The issue is call ordering inside Serialize,\n// not a value's type. There is no runtime type to narrow on.","tryCatchPattern":"// Prefer fixing the Serialize impl. catch_unwind only isolates untrusted impls:\nlet result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {\n    serde_json::to_value(&maybe_buggy_type)\n}));\nmatch result {\n    Ok(Ok(value)) => { /* got a Value */ }\n    Ok(Err(e)) => { /* serialization Error, not the panic */ }\n    Err(_) => { /* serialize_value-before-serialize_key panic: audit the impl */ }\n}","preventionTips":["Use serialize_entry(k, v) instead of separate serialize_key/serialize_value calls.","Never call serialize_value without a preceding serialize_key on the same SerializeMap handle.","Audit any Serializer wrapper/adapter that forwards serialize_value for a skipped serialize_key.","Write a Serialize conformance test that drives to_value on empty and populated instances.","Prefer building serde_json::Value/Map directly over hand-rolling Serialize for simple cases."],"tags":["serde-json","serde","serialize","custom-serialize","panic","internal-contract"],"backgroundTag":null,"analyzedSha":"afdf6fc67247dd7fa4fcde1381e6ecc6bcc7a30e","analyzedAt":"2026-08-08T07:08:57.171Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}