serde-rs/json · error
no entry found for key
Error message
no entry found for key
What it means
This is a deliberate panic from serde_json's IndexMut impl for Map<String, Value> at src/map.rs:479, which calls self.map.get_mut(index).expect("no entry found for key"). Mutably indexing a Map with a key that is absent is treated like indexing a Vec out of bounds: there is no valid place to write, so the process panics rather than silently inserting or returning a default. It is by design that Map implements IndexMut (unlike std HashMap/BTreeMap), so the indexing operator map["k"] = v is only safe when the key already exists; for insert-or-update you must use the entry/insert API. The panic message is the literal string passed to expect.
Solutions
- Replace the mutable index assignment map["k"] = v with map.insert("k".to_owned(), v), which inserts if absent and overwrites if present.
- If you only want to update an existing entry, guard with map.get_mut("k") and act on the Option<&mut Value>, or use the entry API: map.entry("k".to_owned()).or_insert(default).
- Check presence first with map.contains_key("k") and handle the absent case explicitly rather than letting indexing panic.
- When the key is dynamic/external, normalize and validate it (trim, casing, allow-list) before using it as an index.
- Enable clippy (clippy::indexing_slicing) and review any map[k] = sites against a known key set.
Example fix
// before
let mut obj = serde_json::Map::new();
obj["missing"] = serde_json::json!(42); // panics: no entry found for key
// after
obj.insert("missing".to_owned(), serde_json::json!(42));
// or, update-only without panicking:
if let Some(slot) = obj.get_mut("missing") {
*slot = serde_json::json!(42);
} Defensive patterns
Strategy: validation
Validate before calling
// Check before mutating via [] to avoid the panic at src/map.rs:479.
fn set_or_skip(map: &mut serde_json::Map<String, serde_json::Value>,
key: &str, val: serde_json::Value) {
if map.contains_key(key) {
map[key] = val;
} else {
// decide: insert, log, or skip
map.insert(key.to_owned(), val);
}
}
// Prefer the entry API for insert-or-update in one call:
// map.entry(key.to_owned()).or_insert(default_value); Type guard
// Not a type-narrowing issue; the guard is key presence, not a type.
// Helper that returns Option so the caller handles absence explicitly:
fn get_slot<'a>(map: &'a mut serde_json::Map<String, serde_json::Value>,
key: &str) -> Option<&'a mut serde_json::Value> {
map.get_mut(key)
} Try / catch
// Rust: panics are not Result. Avoid catch_unwind for control flow; validate instead.
// Last-resort isolation (e.g. running untrusted config transforms):
let outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
map["maybe_absent"] = serde_json::json!(1);
}));
if outcome.is_err() { /* key was absent: handle */ } Prevention
- Prefer Map::insert / Map::entry over the [] = sugar whenever the key may be absent.
- Treat keys derived from external input (env, request, config) as untrusted: normalize and validate before indexing.
- Enable clippy::indexing_slicing to flag [] usage on maps for review.
- Write a unit test that exercises the absent-key path, not just the present-key fixture.
- Keep a documented allow-list of expected object keys near the indexing site.
When it happens
Trigger: Calling map["someKey"] = serde_json::Value::Null (or any assignment through []) when "someKey" is not present in the Map. Triggered by IndexMut::index_mut at src/map.rs:478-480 any time get_mut returns None — e.g. the key was never inserted, was removed, or a typo'd/dynamic key (from env vars, HTTP request data, or config files) does not match any existing key.
Common situations: Building JSON output where a field name comes from external/untrusted input (request bodies, env vars) and is assumed to exist. Assuming a parsed JSON object always contains a key that the source occasionally omits. Refactoring that renames a field in one place but not the indexing site. Hot-path code that switched from map.get_mut to the [] sugar for brevity. Tests passing because the fixture always had the key, then panicking in production on a sparser document.
Related errors
AI-assisted analysis of serde-rs/json@afdf6fc672 (2026-08-08).
Data as JSON: /api/errors/4433e25861dd3383.
Report an issue: GitHub.
Appendix: 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 afdf6fc672)