FasterXML/jackson-databind · error · IllegalStateException
Cannot update Map.Entry values
Error message
Cannot update Map.Entry values
What it means
Map.Entry values are treated as immutable by the default MapEntryDeserializer; the three-argument deserialize(p, ctxt, resultToUpdate) override unconditionally throws IllegalStateException. Jackson's 'update/merge' API (readerForUpdating, updateValue) requires the deserializer to support in-place updates, which a single-entry key/value pair cannot do meaningfully.
Solutions
- Replace Map.Entry with a mutable pair type (e.g. AbstractMap.SimpleEntry, or a custom Pair class with setters).
- Deserialize into a fresh Map.Entry each time instead of updating an existing one.
- If merging is required, deserialize to a Map and construct a new entry from the merged result.
Example fix
// before Map.Entry<String,Integer> entry = ...; entry = mapper.readerForUpdating(entry).readValue(json); // throws // after AbstractMap.SimpleEntry<String,Integer> entry = ...; entry = mapper.readerForUpdating(new AbstractMap.SimpleEntry<>(entry.getKey(), entry.getValue())).readValue(json);
Defensive patterns
Strategy: try-catch
Validate before calling
if (target instanceof Map.Entry && operation == UPDATE) {
throw new UnsupportedOperationException(
"Map.Entry is immutable; use AbstractMap.SimpleEntry for updates");
} Type guard
boolean isUpdatablePairType(Class<?> c) {
return c != null
&& !Map.Entry.class.isAssignableFrom(c);
} Try / catch
try {
mapper.readerForUpdating(entry).readValue(json);
} catch (IllegalStateException e) {
if (e.getMessage().contains("Map.Entry")) {
// fall back to fresh deserialization
entry = mapper.readValue(json, new TypeReference<Map.Entry<K,V>>(){});
} else throw e;
} Prevention
- Use AbstractMap.SimpleEntry (or a custom Pair) whenever you need readerForUpdating/updateValue.
- Static-check that no Map.Entry-typed field is passed to updateValue.
- Document immutability of Map.Entry in domain model guides.
When it happens
Trigger: Calling objectMapper.readerForUpdating(existingEntry).readValue(json) where the target is a Map.Entry; invoking mapper.updateValue(entry, jsonNode) with a Map.Entry instance; using ObjectReaderToUpdate patterns against a Map.Entry-typed property.
Common situations: Treating Map.Entry as a mutable POJO; migrating code that used a Pair-like class to Map.Entry; JSON patch/merge APIs applied to entry types; frameworks that generically call updateValue on every field type.
Related errors
- Cannot update `Map.Entry` values
- Missing generic type information for "+type
- AnnotationIntrospector returned `Class<
- AnnotationIntrospector returned Class
- AnnotationIntrospector returned Class
AI-assisted analysis of FasterXML/jackson-databind@87876ca5c0 (2026-08-11).
Data as JSON: /api/errors/5ee26272ea2f1ecb.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/tools/jackson/databind/deser/jdk/MapEntryDeserializer.java:311
if (t == JsonToken.PROPERTY_NAME) { // most likely
ctxt.reportInputMismatch(this,
"Problem binding JSON into Map.Entry: more than one entry in JSON (second field: '%s')",
p.currentName());
} else {
// how would this occur?
ctxt.reportInputMismatch(this,
"Problem binding JSON into Map.Entry: unexpected content after JSON Object entry: "+t);
}
return null;
}
return new AbstractMap.SimpleEntry<Object,Object>(key, value);
}
@Override
public Map.Entry<Object,Object> deserialize(JsonParser p, DeserializationContext ctxt,
Map.Entry<Object,Object> result) throws JacksonException
{
throw new IllegalStateException("Cannot update Map.Entry values");
}
@Override
public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
TypeDeserializer typeDeserializer)
throws JacksonException
{
// In future could check current token... for now this should be enough:
return typeDeserializer.deserializeTypedFromObject(p, ctxt);
}
/*
/**********************************************************************
/* Alternate handlers
/**********************************************************************
*/
/**View on GitHub (pinned to 87876ca5c0)