FasterXML/jackson-databind · error · IllegalArgumentException
Trying to resolve a forward reference with id [${id}] that w
Error message
Trying to resolve a forward reference with id [${id}] that wasn't previously seen as unresolved. What it means
ObjectIdReferenceProperty.PropertyReferring throws when handleResolvedForwardReference is invoked with an id that was not recorded as an unresolved forward reference. The property tracks pending ids; resolving an id the resolver never saw indicates the caller or stream is out of sync with the recorded pending set.
Source
Thrown at src/main/java/tools/jackson/databind/deser/impl/ObjectIdReferenceProperty.java:135
public final static class PropertyReferring extends Referring {
private final ObjectIdReferenceProperty _parent;
public final Object _pojo;
public PropertyReferring(ObjectIdReferenceProperty parent,
UnresolvedForwardReference ref, Class<?> type, Object ob)
{
super(ref, type);
_parent = parent;
_pojo = ob;
}
@Override
public void handleResolvedForwardReference(DeserializationContext ctxt,
Object id, Object value) throws JacksonException
{
if (!hasId(id)) {
throw new IllegalArgumentException("Trying to resolve a forward reference with id [" + id
+ "] that wasn't previously seen as unresolved.");
}
// [databind#1496]: forward ref resolved, remove from pending set
ctxt.removePendingForwardRef(_pojo);
_parent.set(ctxt, _pojo, value);
}
}
}
View on GitHub (pinned to 87876ca5c0)
Solutions
- Let Jackson resolve object ids through the normal parse path; do not call handleResolvedForwardReference manually.
- If resolving manually, guard with hasId(id) before calling and skip unknown ids.
- Ensure the same ObjectMapper (same identity scheme) produces and consumes the JSON.
- Avoid sharing ObjectIdResolver state across concurrent parse operations.
Example fix
// before
referring.handleResolvedForwardReference(ctxt, id, value); // id not pending -> throws
// after
if (referring.hasId(id)) {
referring.handleResolvedForwardReference(ctxt, id, value);
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!referring.hasId(id)) {
// id never pending; skip
return;
} Type guard
// no type guard; runtime membership via hasId
Try / catch
try { referring.handleResolvedForwardReference(ctxt, id, value); }
catch (IllegalArgumentException e) {
if (e.getMessage().contains("wasn't previously seen as unresolved")) {
// log and skip
} else throw e;
} Prevention
- Avoid manual forward-reference resolution; let Jackson drive it.
- Guard every manual handleResolvedForwardReference call with hasId.
When it happens
Trigger: Manually driving ObjectIdResolver resolution with ids not produced during parsing; concurrent deserializers sharing state; a stream where an @id is referenced as resolved but was never seen as pending; partial replay of recorded forward references.
Common situations: Custom ObjectIdResolver implementations that call handleResolvedForwardReference directly; @JsonIdentityInfo combined with custom streaming code that bypasses normal id bookkeeping; tests that synthesize UnresolvedForwardReference instances.
Related errors
- Trying to resolve a forward reference with id [${id.toString
- Could not resolve Object Id [${id}] (for ${_beanType}).
- %d deserialization problems%s (showing first 5):%n%s
- Could not resolve Object Id [{}] -- unresolved forward-refer
- maxProblems must be positive, was: ${maxProblems}
AI-assisted analysis of FasterXML/jackson-databind@87876ca5c0 (2026-08-11).
Data as JSON: /api/errors/5581c664d2fac0f5.
Report an issue: GitHub.