Jackson 2.18.3 fails to deserialize classes with @Builder (Lombok) and ImmutableList (Guava) fields. This worked in Jackson 2.18.1 but breaks after upgrading to 2.18.3 (via Spring Boot 3.4.0 → 3.4.5).

Error

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: 
Cannot construct instance of com.google.common.collect.ImmutableList 
(no Creators, like default constructor, exist)

Code that fails

@Builder
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = PRIVATE)
class MimirAssertsRelabelRules {
    private ImmutableList<MimirRelabelRuleGroup> asserts;
}

ObjectMapper mapper = new ObjectMapper()
    .registerModule(new GuavaModule());

// This throws InvalidDefinitionException
mapper.readValue(json, MimirAssertsRelabelRules.class);

Workaround that works

Add @JsonCreator to force constructor-based deserialization:

@Builder
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = PRIVATE)
class MimirAssertsRelabelRules {
    private ImmutableList<MimirRelabelRuleGroup> asserts;

    @JsonCreator
    public static MimirAssertsRelabelRules fromJson(@JsonProperty("asserts") List<MimirRelabelRuleGroup> asserts) {
        return new MimirAssertsRelabelRules(
            asserts != null ? ImmutableList.copyOf(asserts) : ImmutableList.of()
        );
    }
}

This works because it bypasses the builder pattern and uses constructor-based deserialization.

Question

Can anyone help explain why this changed between 2.18.1 and 2.18.3?

Comment From: pjfanning

The 2.18 release notes are at https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.18

Maybe, you could try 2.18.5.

Comment From: JooHyukKim

Guava version change reports also has no diff between 2.18.1 and 2.18.3.

So we can basically narrow down to databind module only, BUT it's reasonable try with Jackson-ONLY reproduction @krritik if you could provide one?

Comment From: cowtowncoder

Wrong repo, as per README should not file issues here. Will transfer to proper repo.

Comment From: cowtowncoder

Hmmmh. So, need to de-Lombok. But unfortunately there's the Guava part so may need to actually move to

https://github.com/FasterXML/jackson-datatypes-collections/

Aside from that would second suggestion to try 2.18.5 and if that won't work, 2.19.3 or 2.20.1.

Finally, aside from seeing de-Lombokified sources, actual exception would be useful.

Comment From: krritik

Thanks @cowtowncoder for moving the issue to the correct repo.

I tried 2.18.5, 2.19.3 and 2.20.1 None worked for now. I will update it with the exact error.

@JooHyukKim , let me try if I can put a more simpler Jackson-ONLY reproduction

Comment From: krritik

Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.google.common.collect.ImmutableList` (no Creators, like default constructor, exist): no default no-arguments constructor found
 at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 2, column: 5] (through reference chain: ai.asserts.prometheus.definition.rules.MimirAssertsRelabelRules["asserts"])
    at app//com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
    at app//com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1888)
    at app//com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:414)
    at app//com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1375)
    at app//com.fasterxml.jackson.databind.deser.ValueInstantiator.createUsingDefault(ValueInstantiator.java:248)
    at app//com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createUsingDefault(StdValueInstantiator.java:275)
    at app//com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.createDefaultInstance(CollectionDeserializer.java:264)
    at app//com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:246)
    at app//com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:30)
    at app//com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138)
    at app//com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:399)
    at app//com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
    at app//com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:342)
    at app//com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:2131)
    at app//com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1501)
    at app//org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:397)
    ... 45 more

This is the error trace for now. If it is of any help.

Comment From: JooHyukKim

If adding Creator works, small posibility its Guava issue.

Let's wait for de Lomboked structure and continue investigating further

Comment From: cowtowncoder

Problem could be due to

    at app//org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:397)

that is, depends on details of how Spring calls Jackson.

But yes, we need a stand-alone reproduction and can then figure it out.

Comment From: JooHyukKim

at app//org.springframework.http.converter

This could mean Guava module not rehistered at all...

Re-reading the Creator method part , List is used, not directly Guava's immutablelist. I did not expect that.

@krritik Change ur creator parameter directly to Guava's ImmutableList and see what error throws

Comment From: cowtowncoder

@JooHyukKim You may be looking at "work-around" case: initial one does have it:

@Builder
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = PRIVATE)
class MimirAssertsRelabelRules {
    private ImmutableList<MimirRelabelRuleGroup> asserts;
}

same wrt GuavaModule.