FasterXML/jackson-databind · error · UnresolvedForwardReference

Could not resolve Object Id

Error message

Could not resolve Object Id [{}] -- unresolved forward-reference?

What it means

Thrown as UnresolvedForwardReference by AbstractDeserializer._deserializeFromObjectId when a referenced Object Id (from @JsonIdentityInfo) cannot be resolved by the time it is needed, meaning the defining object never appeared (or appeared later than the reference). This indicates a data/integrity problem: the JSON contains an @id reference with no matching @id definition earlier in the stream.

Solutions

  1. Ensure the JSON defines every object (with its @id) before any reference to it; reorder the payload or pre-seed the referenced objects.
  2. Validate payload integrity upstream before handing it to Jackson.
  3. If references can legitimately be unknown, register a DeserializationProblemHandler (or use readValueAs(Iterator)/ObjectMapper.readerForUpdating) to handle or skip unknown ids.
  4. Verify @JsonIdentityInfo is configured consistently on both serialization and deserialization sides (generator, resolver, scope).

Example fix

// before: reference 2 appears, but object with @id=2 is missing/after
[{"@id":1,"name":"a","ref":2}]

// after: define id 2 first (or include it in the stream)
[{"@id":2,"name":"b"},{"@id":1,"name":"a","ref":2}]
Defensive patterns

Strategy: try-catch

Validate before calling

// Before deserializing, validate that every referenced @id has a matching definition in the payload (application-level check).
Set<Object> defined = collectDefinedIds(json);
Set<Object> referenced = collectReferencedIds(json);
if (!defined.containsAll(referenced)) throw new IllegalStateException("Unresolved ids: " + difference);

Try / catch

try {
    mapper.readValue(json, Node.class);
} catch (UnresolvedForwardReference e) {
    // log the missing id (e.getRoid().hasId()) and handle/report
}

Prevention

When it happens

Trigger: Deserializing JSON that uses @JsonIdentityInfo where an object references an id that does not exist in the stream, or that appears after the reference (forward reference that never resolves before the parser reaches the end of the relevant scope).

Common situations: Bi-directional relationships serialized with ids where one side was dropped; partial/truncated JSON payloads; reordered fields where the referenced entity is in a sibling not yet parsed; integrating with a store that emits forward references.

Related errors


AI-assisted analysis of FasterXML/jackson-databind@87876ca5c0 (2026-08-11). Data as JSON: /api/errors/4662d89a82b67a41. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/tools/jackson/databind/deser/AbstractDeserializer.java:315

            }
            break;
        }
        return null;
    }

    /**
     * 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+"] -- unresolved forward-reference?",
                    p.currentLocation(), roid);
        }
        return pojo;
    }
}

View on GitHub (pinned to 87876ca5c0)