FasterXML/jackson-databind · critical · IllegalStateException
Should never deserialize `{}` directly
Error message
Should never deserialize `{}` directly What it means
Thrown by ObjectMapper.readResolve() as a JDK serialization safety guard. ObjectMapper serializes via writeReplace() into a MapperBuilderState object, never as a raw ObjectMapper. If a deserialized stream somehow contains a serialized ObjectMapper (rather than MapperBuilderState), readResolve() blocks reconstruction. This prevents corrupted or manually crafted streams from producing an incompletely initialized mapper.
Source
Thrown at src/main/java/tools/jackson/databind/ObjectMapper.java:394
/**********************************************************************
/* Life-cycle: JDK serialization support
/**********************************************************************
*/
// Logic here is simple: instead of serializing mapper via its contents,
// we have pre-packaged `MapperBuilderState` in a way that makes serialization
// easier, and we go with that.
// But note that return direction has to be supported, then, by that state object
// and NOT anything in here.
@Serial
protected Object writeReplace() {
return _savedBuilderState;
}
// Just as a sanity check verify there is no attempt at directly instantiating mapper here
@Serial
protected Object readResolve() {
throw new IllegalStateException("Should never deserialize `"+getClass().getName()+"` directly");
}
/*
/**********************************************************************
/* Versioned impl
/**********************************************************************
*/
/**
* Method that will return version information stored in and read from jar
* that contains this class.
*/
@Override
public Version version() {
return tools.jackson.databind.cfg.PackageVersion.VERSION;
}
/*View on GitHub (pinned to 87876ca5c0)
Solutions
- Do not serialize/deserialize ObjectMapper directly; it is immutable and thread-safe in 3.x — construct it once and share the reference.
- If you must pass mapper configuration across a boundary, serialize the MapperBuilderState obtained from writeReplace, or pass JSON/text configuration and rebuild on the other side.
- If using a session/cache that serializes, mark the ObjectMapper field transient and reconstruct it in readResolve/postRead.
Example fix
// before
public class MyService implements Serializable {
private ObjectMapper mapper = new JsonMapper(); // gets serialized
}
// after
public class MyService implements Serializable {
private transient ObjectMapper mapper;
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
mapper = new JsonMapper();
}
} Defensive patterns
Strategy: validation
Validate before calling
// Do not serialize ObjectMapper. If in a Serializable class: // mark transient and reconstruct private transient ObjectMapper mapper = new JsonMapper();
Try / catch
try {
Object o = objectInputStream.readObject();
} catch (IllegalStateException e) {
if (e.getMessage().contains("Should never deserialize")) {
// reconstruct mapper from builder state or config instead
}
} Prevention
- Never serialize ObjectMapper; construct it once and share.
- Mark ObjectMapper fields transient in Serializable classes and rebuild in readObject.
- Pass configuration as data, not as a serialized mapper instance.
When it happens
Trigger: Java serialization/deserialization of an ObjectMapper instance where the stream contains the raw ObjectMapper class rather than the MapperBuilderState replacement. This can happen if writeReplace() was bypassed (e.g., via ObjectOutputStream.writeObjectOverride in a subclass) or if a stream was crafted externally.
Common situations: Storing an ObjectMapper in an HttpSession or Java EE distributed cache that serializes it. Using a custom serialization framework that bypasses writeReplace. Inheriting from ObjectMapper and overriding writeReplace/readResolve incorrectly. Extremely rare in normal usage since writeReplace prevents it.
Related errors
- Can only call after BeanDeserializer has been resolved
- Missing constructor (broken JDK (de)serialization?)
- Cannot update Map.Entry values
- Cannot update `Map.Entry` values
AI-assisted analysis of FasterXML/jackson-databind@87876ca5c0 (2026-08-11).
Data as JSON: /api/errors/804d35396702074b.
Report an issue: GitHub.