FasterXML/jackson-databind · error · IllegalStateException

Cannot update `Map.Entry` values

Error message

Cannot update `Map.Entry` values

What it means

The POJO-shaped variant of Map.Entry deserialization (MapEntryDeserializer.POJOWrappedDeserializer, used when Map.Entry is rendered as a {"key":...,"value":...} object) also rejects in-place updates: its deserialize(p, ctxt, resultToUpdate) throws IllegalStateException. Same immutability contract as the default deserializer, applied to the shape-shifting path.

Solutions

  1. Use a mutable holder (AbstractMap.SimpleEntry or a dedicated Pair class) for any field that needs updates.
  2. Avoid readerForUpdating/updateValue on Map.Entry-typed properties; deserialize fresh each time.
  3. Force the non-POJO shape (or vice versa) only if it helps you avoid the update path; the immutability restriction applies to both shapes.

Example fix

// before
public class PairBox { public Map.Entry<String,Integer> pair; }
PairBox box = ...;
box = mapper.updateValue(box, patchJson); // throws when updating pair

// after
public class PairBox { public AbstractMap.SimpleEntry<String,Integer> pair; }
Defensive patterns

Strategy: try-catch

Validate before calling

if (target instanceof Map.Entry && operation == UPDATE) {
    throw new UnsupportedOperationException(
        "POJO-wrapped Map.Entry is immutable; use AbstractMap.SimpleEntry");
}

Type guard

boolean isUpdatablePairType(Class<?> c) {
    return c != null && !Map.Entry.class.isAssignableFrom(c);
}

Try / catch

try {
    mapper.updateValue(holder, patchJson);
} catch (IllegalStateException e) {
    if (e.getMessage().contains("Map.Entry")) {
        holder.pair = mapper.convertValue(patchNode.get("pair"),
            new TypeReference<AbstractMap.SimpleEntry<K,V>>(){});
    } else throw e;
}

Prevention

When it happens

Trigger: Calling updateValue or readerForUpdating against a Map.Entry that Jackson deserializes via the POJO-wrapped shape (e.g. when SPLIT shapes or specific config forces object-form reading); JSON-patch tooling that merges into a Map.Entry field.

Common situations: Configuring DeserializationFeature/JsonFormat shapes that trigger POJO wrapping for entries; reusing a deserialized entry across multiple update cycles; generic merge frameworks that recurse into all property types including entries.

Related errors


AI-assisted analysis of FasterXML/jackson-databind@87876ca5c0 (2026-08-11). Data as JSON: /api/errors/779760419e063947. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/tools/jackson/databind/deser/jdk/MapEntryDeserializer.java:522

                }

                t = p.nextToken(); // move to next property or END_OBJECT
            }

            if (t != JsonToken.END_OBJECT) {
                ctxt.reportInputMismatch(this,
                        "Problem deserializing `Map.Entry`; unexpected content after Object value: "
                                +JsonToken.valueDescFor(t));
            }

            return new AbstractMap.SimpleEntry<>(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);
        }

        // Copied from `ContainerDeserializerBase`
        protected <BOGUS> BOGUS wrapAndThrow(DeserializationContext ctxt,
                Throwable t, Object ref, String key) throws JacksonException
        {
            while (t instanceof InvocationTargetException && t.getCause() != null) {
                t = t.getCause();
            }

View on GitHub (pinned to 87876ca5c0)