serde-rs/json · error
no entry found for key
Error message
no entry found for key
What it means
This is a panic raised by serde_json's IndexMut impl for Map<String, Value> at src/map.rs:479. The line `self.map.get_mut(index).expect("no entry found for key")` runs when you assign into a map via the `map[key] = value` syntax; if the key is not already present, get_mut returns None and expect aborts the process. It mirrors the panic semantics of BTreeMap/IndexMut: indexing is for read/write access to EXISTING entries, not for insertion. Use a fallible accessor (get_mut, entry, insert) when the key may be absent.
Source
Thrown at src/map.rs:479
/// Mutably access an element of this map. Panics if the given key is not
/// present in the map.
///
/// ```
/// # use serde_json::json;
/// #
/// # let mut map = serde_json::Map::new();
/// # map.insert("key".to_owned(), serde_json::Value::Null);
/// #
/// map["key"] = json!("value");
/// ```
impl<Q> ops::IndexMut<&Q> for Map<String, Value>
where
String: Borrow<Q>,
Q: ?Sized + Ord + Eq + Hash,
{
fn index_mut(&mut self, index: &Q) -> &mut Value {
self.map.get_mut(index).expect("no entry found for key")
}
}
impl Debug for Map<String, Value> {
#[inline]
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
self.map.fmt(formatter)
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
impl serde::ser::Serialize for Map<String, Value> {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
use serde::ser::SerializeMap;View on GitHub (pinned to a3e9758ffc)
Solutions
- Replace `map[key] = value` with `map.insert(key.to_owned(), value)` when you want to create-or-overwrite the entry.
- Use `map.entry(key).or_insert(value)` when you want to ensure a default and then mutate the returned slot.
- If you only want to mutate an existing entry, use `if let Some(slot) = map.get_mut(key) { *slot = value; }` to skip absent keys safely.
- When the Map comes from a parsed Value, narrow it first: `if let Value::Object(map) = &mut val { ... }` and validate the key with `map.contains_key(key)` before indexing.
- Audit the payload with a typed deserializer (serde_derive struct with Option<T> for optional fields) so missing keys surface as None instead of panicking on assignment.
Example fix
// before — panics when "timeout" is absent
let mut v: serde_json::Value = serde_json::from_str(input)?;
v["timeout"] = serde_json::json!(30);
// after — create-or-overwrite without panicking
use serde_json::Value;
let mut v: Value = serde_json::from_str(input)?;
match v.as_object_mut() {
Some(map) => {
map.entry("timeout".to_owned())
.or_insert(Value::Null);
map["timeout"] = json!(30);
}
None => return Err("expected a JSON object".into()),
} Defensive patterns
Strategy: validation
Validate before calling
use serde_json::Map;
/// Safe setter that never panics — inserts if missing, overwrites if present.
fn ensure_set(map: &mut Map<String, serde_json::Value>, key: &str, val: serde_json::Value) {
map.insert(key.to_owned(), val);
}
/// Safe conditional mutate — no-op when the key is absent.
fn mutate_existing(map: &mut Map<String, serde_json::Value>, key: &str, val: serde_json::Value) {
if let Some(slot) = map.get_mut(key) {
*slot = val;
}
}
/// Guard before indexing: refuse to call IndexMut unless key is present.
fn check_before(map: &Map<String, serde_json::Value>, key: &str) -> bool {
map.contains_key(key)
} Type guard
use serde_json::{Map, Value};
/// Narrows a Value to a mutable Object map; returns None for non-objects.
fn as_object_mut_guard(v: &mut Value) -> Option<&mut Map<String, Value>> {
match v {
Value::Object(map) => Some(map),
_ => None,
}
}
/// Returns true only when indexing with this key is panic-safe.
fn key_present(map: &Map<String, Value>, key: &str) -> bool {
map.contains_key(key)
} Prevention
- Treat `map[key] = v` as a read-modify operation, not an insert — use `Map::insert` or `Map::entry` to create new keys.
- Before destructuring a parsed Value with indexing, narrow with `as_object_mut()` and check `contains_key`.
- Define the JSON contract with serde structs (Option<T> for optional fields) instead of ad-hoc Map mutation.
- Add unit tests that exercise the absent-key path; the panic only fires at runtime under missing data.
- Enable clippy::indexing_slicing (or forbid_unsafe-style discipline) in CI to catch indexing on Option-returning accessors.
When it happens
Trigger: Calling `obj["field"] = json!(...)` or `&mut obj["field"]` on a serde_json::Map / Value::Object whose key does not exist. Also reachable via `serde_json::Value` indexing when the inner object lacks the requested key. Differs from `Map::insert` (which creates the entry) and from `Map::get_mut` (which returns Option). The same syntax on serde_json::Value for a non-object variant yields a different panic ("not an object"), so this specific message always means: the Map lookup missed.
Common situations: 1) Assuming a JSON payload always carries an optional field, then writing into it without inserting first. 2) Building up a response object field-by-field with `map["k"] = v` instead of `insert`. 3) Schema/version drift: server stopped returning a key the client expected to mutate. 4) Typos or case mismatch in the key string (JSON keys are case-sensitive). 5) Mutating a Map produced from `Value::as_object_mut()` after the field was filtered/renamed upstream.
AI-assisted analysis of serde-rs/json@a3e9758ffc (2026-08-06).
Data as JSON: /data/errors/4433e25861dd3383.json.
Report an issue: GitHub.