FasterXML/jackson-databind · error · IllegalArgumentException

Trying to resolve a forward reference with id [{}] that wasn

Error message

Trying to resolve a forward reference with id [{}] that wasn't previously seen as unresolved.

What it means

Thrown by ObjectIdReferenceProperty.PropertyReferring.handleResolvedForwardReference when attempting to resolve a forward reference by an id that was never registered as pending. The PropertyReferring object (created for properties annotated with @JsonObjectIdReferenceProperty) checks hasId(id) and rejects ids it doesn't recognize. This indicates a mismatch between the ids being resolved and those previously tracked during deserialization.

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

Solutions

  1. Ensure forward references are resolved in the same order they were encountered during deserialization.
  2. Verify the @JsonIdentityInfo generator produces ids consistent with what appears in the JSON.
  3. If resolving manually, only pass ids that were returned by the framework's unresolved reference tracking.
  4. Check for truncated or malformed JSON that might introduce spurious id references.

Example fix

// before: resolving id "99" that was never seen
roid.handleResolvedForwardReference(ctxt, "99", value);
// after: only resolve ids that were registered
if (roid.hasId(id)) {
    roid.handleResolvedForwardReference(ctxt, id, value);
}
Defensive patterns

Strategy: validation

Validate before calling

// Before resolving, check hasId
if (referring.hasId(id)) {
    referring.handleResolvedForwardReference(ctxt, id, value);
}

Type guard

if (roid.hasId(id)) {
    roid.handleResolvedForwardReference(ctxt, id, value);
}

Try / catch

try {
    referring.handleResolvedForwardReference(ctxt, id, value);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("wasn't previously seen")) {
        logger.warn("Unknown forward ref id: {}", id);
    } else { throw e; }
}

Prevention

When it happens

Trigger: A property using @JsonIdentityReference (or @JsonIdentityInfo on a property) where the resolveForwardReference API is called with an id not previously encountered. Programmatic forward-reference resolution out of order. JSON with object id references whose ids don't match any pending unresolved references.

Common situations: Using @JsonIdentityInfo with a property-level reference where the JSON stream contains ids that were never registered. Mixing manual and automatic forward-reference resolution. Deserialization order changes causing id mismatches after a refactor.

Related errors


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