FasterXML/jackson-databind · error · IllegalStateException
Can only call after BeanDeserializer has been resolved
Error message
Can only call after BeanDeserializer has been resolved
What it means
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.
Source
Thrown at src/main/java/tools/jackson/databind/deser/bean/BeanDeserializerBase.java:1419
@Override
public JavaType getValueType() { return _beanType; }
@Override // since 2.12
public LogicalType logicalType() {
return LogicalType.POJO;
}
/**
* Accessor for iterating over properties this deserializer uses; with
* the exception that properties passed via Creator methods
* (specifically, "property-based constructor") are not included,
* but can be accessed separate by calling
* {@link #creatorProperties}
*/
public Iterator<SettableBeanProperty> properties()
{
if (_beanProperties == null) {
throw new IllegalStateException("Can only call after BeanDeserializer has been resolved");
}
return _beanProperties.iterator();
}
/**
* Accessor for finding properties that represents values to pass
* through property-based creator method (constructor or
* factory method)
*/
public Iterator<SettableBeanProperty> creatorProperties()
{
if (_propertyBasedCreator == null) {
return Collections.<SettableBeanProperty>emptyList().iterator();
}
return _propertyBasedCreator.properties().iterator();
}
public SettableBeanProperty findProperty(PropertyName propertyName)View on GitHub (pinned to 87876ca5c0)
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.
Example fix
// before BeanDeserializer d = (BeanDeserializer) factory.createDeserializer(ctxt, type); d.properties(); // throws: not resolved // after ValueDeserializer<Object> d = ctxt.findRootValueDeserializer(type); d.resolve(ctxt); d.properties(); // now safe
Defensive patterns
Strategy: validation
Validate before calling
BeanDeserializerBase d = ...;
try {
java.lang.reflect.Field f = BeanDeserializerBase.class.getDeclaredField("_beanProperties");
f.setAccessible(true);
if (f.get(d) == null) {
// not resolved yet; do not call properties()
}
} catch (ReflectiveOperationException e) { /* ignore */ } Type guard
// no type guard; rely on lifecycle ordering
Try / catch
try { d.properties(); }
catch (IllegalStateException e) {
if (e.getMessage().contains("has been resolved")) {
// call resolve(ctxt) first, then retry
} else throw e;
} Prevention
- Obtain deserializers through mapper.getDeserializerFor(...) so resolution has run before inspection.
- In factory hooks, inspect the BeanDeserializerBuilder rather than the constructed deserializer.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Property '${getName()}' already had index (${_propertyIndex}
- Class ${getClass().getName()} does not override `withBeanPro
- No entry '${oldProp.getName()}' found, can't replace
- Missing constructor (broken JDK (de)serialization?)
- Should not call set() on ObjectIdProperty that has no Settab
AI-assisted analysis of FasterXML/jackson-databind@87876ca5c0 (2026-08-11).
Data as JSON: /api/errors/3a2f9bc7f05ce0de.
Report an issue: GitHub.