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

  1. Do not serialize/deserialize ObjectMapper directly; it is immutable and thread-safe in 3.x — construct it once and share the reference.
  2. 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.
  3. 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

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


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