FasterXML/jackson-databind · error · UnresolvedForwardReference

Could not resolve Object Id [{}] (for {}).

Error message

Could not resolve Object Id [{}] (for {}).

What it means

Thrown as an UnresolvedForwardReference by BeanDeserializerBase.deserializeFromObjectId when an object identity reference is encountered (via @JsonIdentityInfo) but the referenced object has not yet been resolved. This means the id was registered with a ReadableObjectId but resolve() returned null — the object was never fully deserialized. Unlike a simple missing id, this specifically creates an UnresolvedForwardReference with location info so it can potentially be resolved later.

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 a50c7d2a1d)

Solutions

  1. Ensure the JSON fully contains all referenced objects — no truncation or missing entries.
  2. Check @JsonIdentityInfo scope matches between serialization and deserialization (scope must be the same class or a common base).
  3. If dealing with forward references, handle UnresolvedForwardReference by calling resolveId on the DeserializationContext after the full document is parsed.
  4. Verify the id generator type (ObjectIdGenerators) is consistent between serialization and deserialization configurations.

Example fix

// before: JSON references id 2 before it appears
{"ref":2}
// after: include the referenced object
{"@id":2,"name":"x","ref":2}
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-validate that the JSON stream contains all referenced ids
JsonNode tree = mapper.readTree(json);
Set<Object> definedIds = extractIds(tree);
Set<Object> referencedIds = extractRefs(tree);
if (!definedIds.containsAll(referencedIds)) {
    referencedIds.removeAll(definedIds);
    throw new IllegalStateException("Missing object ids: " + referencedIds);
}

Try / catch

try {
    return mapper.readValue(json, MyClass.class);
} catch (UnresolvedForwardReference e) {
    // Optionally: attempt to resolve from a secondary source, or report gracefully
    logger.error("Unresolved object id '{}' at {}", e.getUnresolvedId(), e.getLocation());
    throw e;
}

Prevention

When it happens

Trigger: JSON with @JsonIdentityInfo where an object id is referenced before it is defined (true forward reference) and the stream ends without the referenced object appearing. Circular references that are not properly handled. An id generator that produces ids not matching the references.

Common situations: Serializing a graph with bidirectional relationships using @JsonIdentityInfo where the deserialization order does not match. Missing or incomplete JSON (truncated stream). Using PROPERTY-based id generation where the id property is absent or mismatched. Polymorphic type processing interfering with identity resolution.

Related errors


AI-assisted analysis of FasterXML/jackson-databind@a50c7d2a1d (2026-08-06). Data as JSON: /api/errors/a17735df6b965dd5. Report an issue: GitHub.