FasterXML/jackson-databind · error · UnresolvedForwardReference
Could not resolve Object Id [${id}] (for ${_beanType}).
Error message
Could not resolve Object Id [${id}] (for ${_beanType}). What it means
Thrown as an UnresolvedForwardReference when a JSON Object Id referenced in the input could not be resolved by the end of deserialization. With @JsonIdentityInfo, an @id with no preceding object definition leaves a pending forward reference; Jackson raises this when it tries to materialize the referenced object but the resolver has no entry.
Source
Thrown at src/main/java/tools/jackson/databind/deser/bean/BeanDeserializerBase.java:1653
protected Object deserializeWithObjectId(JsonParser p, DeserializationContext ctxt)
throws JacksonException
{
return deserializeFromObject(p, ctxt);
}
/**
* Method called in cases where it looks like we got an Object Id
* to parse and use as a reference.
*/
protected Object deserializeFromObjectId(JsonParser p, DeserializationContext ctxt)
throws JacksonException
{
Object id = _objectIdReader.readObjectReference(p, ctxt);
ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver);
// do we have it resolved?
Object pojo = roid.resolve();
if (pojo == null) { // not yet; should wait...
throw new UnresolvedForwardReference(p,
"Could not resolve Object Id ["+id+"] (for "+_beanType+").",
p.currentLocation(), roid);
}
return pojo;
}
protected Object deserializeFromObjectUsingNonDefault(JsonParser p,
DeserializationContext ctxt)
throws JacksonException
{
// 02-Jul-2024, tatu: [databind#4602] Need to tweak regular and "array" delegating
// Creator handling
final ValueDeserializer<Object> delegateDeser = _delegateDeserializer(p);
if (delegateDeser != null) {
// [databind#5909]: signal delegate-bind-pending so that any ROID
// bound during delegate deserialization retains resolved Referrings
// — needed for collection-property forward refs to be rebound after
// updateObjectId(delegate, bean) below.View on GitHub (pinned to 87876ca5c0)
Solutions
- Ensure the JSON was serialized with the same @JsonIdentityInfo (generator/resolver/property name) as the reader.
- If forward references are legitimate, catch UnresolvedForwardReference and resolve them after the fact via handleResolvedForwardReference.
- Provide a custom ObjectIdResolver (scope=SCOPE_SINGLETON or a custom implementation) that retains resolved objects across the stream.
- Check for truncated/malformed JSON where the referenced object definition is missing.
Example fix
// before
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
class Node { Node next; } // reading JSON whose @ref has no object -> throws
// after (ensure writer used same scheme, or resolve manually)
try {
Node n = mapper.readValue(json, Node.class);
} catch (UnresolvedForwardReference e) {
ReadableObjectId roid = e.getRoid();
roid.bindItem(fallbackObject);
} Defensive patterns
Strategy: try-catch
Validate before calling
// before reading, confirm the resolver can answer; generally not possible // pre-validate by ensuring writer config matches reader @JsonIdentityInfo
Type guard
// no compile-time guard; identity references are runtime-resolved
Try / catch
try { mapper.readValue(json, Node.class); }
catch (UnresolvedForwardReference e) {
ReadableObjectId roid = e.getRoid();
// supply the missing object or report the dangling @id
} Prevention
- Use identical @JsonIdentityInfo on both writer and reader.
- For optional references, provide a custom ObjectIdResolver with sensible defaults.
- Validate the JSON stream is complete (no missing object definitions).
When it happens
Trigger: JSON contains {"@id":99,"@ref":99} where the referenced object was never defined; circular references serialized with an incompatible identity scheme; deserializing a fragment that references an object defined elsewhere in a stream that was not parsed; custom ObjectIdResolver that does not retain objects.
Common situations: Mixing serialized output from one mapper version/config with a reader configured differently; truncated JSON; custom ObjectIdResolver with a scope that discards resolved items; @JsonIdentityInfo on bidirectional entities where only one side was serialized.
Related errors
- Trying to resolve a forward reference with id [${id.toString
- Trying to resolve a forward reference with id [${id}] that w
- Should not call set() on ObjectIdProperty that has no Settab
- Failed to parse Date value '%s': %s
- Could not resolve Object Id [{}] -- unresolved forward-refer
AI-assisted analysis of FasterXML/jackson-databind@87876ca5c0 (2026-08-11).
Data as JSON: /api/errors/c8f41231277f9611.
Report an issue: GitHub.