{"record":{"id":"3a2f9bc7f05ce0de","repo":"FasterXML/jackson-databind","slug":"can-only-call-after-beandeserializer-has-been-reso","errorCode":null,"errorMessage":"Can only call after BeanDeserializer has been resolved","messagePattern":"Can only call after BeanDeserializer has been resolved","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"src/main/java/tools/jackson/databind/deser/bean/BeanDeserializerBase.java","lineNumber":1419,"sourceCode":"    @Override\n    public JavaType getValueType() { return _beanType; }\n\n    @Override // since 2.12\n    public LogicalType logicalType() {\n        return LogicalType.POJO;\n    }\n\n    /**\n     * Accessor for iterating over properties this deserializer uses; with\n     * the exception that properties passed via Creator methods\n     * (specifically, \"property-based constructor\") are not included,\n     * but can be accessed separate by calling\n     * {@link #creatorProperties}\n     */\n    public Iterator<SettableBeanProperty> properties()\n    {\n        if (_beanProperties == null) {\n            throw new IllegalStateException(\"Can only call after BeanDeserializer has been resolved\");\n        }\n        return _beanProperties.iterator();\n    }\n\n    /**\n     * Accessor for finding properties that represents values to pass\n     * through property-based creator method (constructor or\n     * factory method)\n     */\n    public Iterator<SettableBeanProperty> creatorProperties()\n    {\n        if (_propertyBasedCreator == null) {\n            return Collections.<SettableBeanProperty>emptyList().iterator();\n        }\n        return _propertyBasedCreator.properties().iterator();\n    }\n\n    public SettableBeanProperty findProperty(PropertyName propertyName)","sourceCodeStart":1401,"sourceCodeEnd":1437,"githubUrl":"https://github.com/FasterXML/jackson-databind/blob/87876ca5c0569b4933aec2d30d6225e4b9ba3a43/src/main/java/tools/jackson/databind/deser/bean/BeanDeserializerBase.java#L1401-L1437","documentation":"BeanDeserializerBase.properties() requires the deserializer to have been resolved first (the _beanProperties field is null until resolve finishes). Calling properties() before resolution completes returns null and the guard throws to prevent iterating an unfinished deserializer.","triggerScenarios":"Calling deserializer.properties() on a deserializer instance obtained from a factory before resolve() was invoked; introspecting properties of a just-constructed BeanDeserializer inside a custom factory hook that runs before resolution.","commonSituations":"Custom BeanDeserializerFactory hooks (e.g. modifyToDeserializationContext, createBeanDeserializer overrides) that enumerate properties too early; framework code that holds a deserializer reference and queries it before the mapper finished wiring it; tests constructing deserializers manually.","solutions":["Do not enumerate properties before resolve(DeserializationContext) has been called; let the ObjectMapper drive the lifecycle.","If you must inspect properties in a factory hook, use the BeanDeserializerBuilder's property list instead of the constructed deserializer's properties().","For tests, obtain the deserializer via mapper.readerFor(X).readValue(...) or mapper.getDeserializerFor(X.class) so resolution has run."],"exampleFix":"// before\nBeanDeserializer d = (BeanDeserializer) factory.createDeserializer(ctxt, type);\nd.properties(); // throws: not resolved\n\n// after\nValueDeserializer<Object> d = ctxt.findRootValueDeserializer(type);\nd.resolve(ctxt);\nd.properties(); // now safe","handlingStrategy":"validation","validationCode":"BeanDeserializerBase d = ...;\ntry {\n    java.lang.reflect.Field f = BeanDeserializerBase.class.getDeclaredField(\"_beanProperties\");\n    f.setAccessible(true);\n    if (f.get(d) == null) {\n        // not resolved yet; do not call properties()\n    }\n} catch (ReflectiveOperationException e) { /* ignore */ }","typeGuard":"// no type guard; rely on lifecycle ordering","tryCatchPattern":"try { d.properties(); }\ncatch (IllegalStateException e) {\n    if (e.getMessage().contains(\"has been resolved\")) {\n        // call resolve(ctxt) first, then retry\n    } else throw e;\n}","preventionTips":["Obtain deserializers through mapper.getDeserializerFor(...) so resolution has run before inspection.","In factory hooks, inspect the BeanDeserializerBuilder rather than the constructed deserializer."],"tags":["deserialization","lifecycle","bean-deserializer","internal"],"backgroundTag":null,"analyzedSha":"87876ca5c0569b4933aec2d30d6225e4b9ba3a43","analyzedAt":"2026-08-11T12:55:24.033Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}