{"record":{"id":"804d35396702074b","repo":"FasterXML/jackson-databind","slug":"should-never-deserialize-directly","errorCode":null,"errorMessage":"Should never deserialize `{}` directly","messagePattern":"Should never deserialize `(.+?)` directly","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"critical","filePath":"src/main/java/tools/jackson/databind/ObjectMapper.java","lineNumber":394,"sourceCode":"    /**********************************************************************\n    /* Life-cycle: JDK serialization support\n    /**********************************************************************\n     */\n\n    // Logic here is simple: instead of serializing mapper via its contents,\n    // we have pre-packaged `MapperBuilderState` in a way that makes serialization\n    // easier, and we go with that.\n    // But note that return direction has to be supported, then, by that state object\n    // and NOT anything in here.\n    @Serial\n    protected Object writeReplace() {\n        return _savedBuilderState;\n    }\n\n    // Just as a sanity check verify there is no attempt at directly instantiating mapper here\n    @Serial\n    protected Object readResolve() {\n        throw new IllegalStateException(\"Should never deserialize `\"+getClass().getName()+\"` directly\");\n    }\n\n    /*\n    /**********************************************************************\n    /* Versioned impl\n    /**********************************************************************\n     */\n\n    /**\n     * Method that will return version information stored in and read from jar\n     * that contains this class.\n     */\n    @Override\n    public Version version() {\n        return tools.jackson.databind.cfg.PackageVersion.VERSION;\n    }\n\n    /*","sourceCodeStart":376,"sourceCodeEnd":412,"githubUrl":"https://github.com/FasterXML/jackson-databind/blob/87876ca5c0569b4933aec2d30d6225e4b9ba3a43/src/main/java/tools/jackson/databind/ObjectMapper.java#L376-L412","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\npublic class MyService implements Serializable {\n    private ObjectMapper mapper = new JsonMapper(); // gets serialized\n}\n// after\npublic class MyService implements Serializable {\n    private transient ObjectMapper mapper;\n    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {\n        in.defaultReadObject();\n        mapper = new JsonMapper();\n    }\n}","handlingStrategy":"validation","validationCode":"// Do not serialize ObjectMapper. If in a Serializable class:\n// mark transient and reconstruct\nprivate transient ObjectMapper mapper = new JsonMapper();","typeGuard":null,"tryCatchPattern":"try {\n    Object o = objectInputStream.readObject();\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"Should never deserialize\")) {\n        // reconstruct mapper from builder state or config instead\n    }\n}","preventionTips":["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."],"tags":["jdk-serialization","objectmapper","lifecycle","immutability"],"backgroundTag":null,"analyzedSha":"87876ca5c0569b4933aec2d30d6225e4b9ba3a43","analyzedAt":"2026-08-11T12:55:24.033Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}