Skip to content

[BUG] LiveRequest.fromJsonString cannot deserialize any LiveRequest (@JsonCreator declared outside Builder) #1274

@SuperCorleone

Description

@SuperCorleone

Describe the Bug

LiveRequest is annotated @JsonDeserialize(builder = LiveRequest.Builder.class), so Jackson must instantiate the AutoValue Builder via a @JsonCreator factory. That factory (jacksonBuilder()) is declared in the enclosing LiveRequest class instead of inside the Builder class, so Jackson finds no creator and throws. As a result LiveRequest.fromJsonString(...) (a public API) fails for every input — even the JSON produced by LiveRequest.toJson(). Serialization works; the object can be written but never read back.

Root Cause

@AutoValue.Builder
@JsonPOJOBuilder(buildMethodName = "build", withPrefix = "")
public abstract static class Builder {
    ...
    public final LiveRequest build() { ... }
}                                   // <-- Builder ends here

public static Builder builder() { return new AutoValue_LiveRequest.Builder().close(false); }
public abstract Builder toBuilder();
public static LiveRequest fromJsonString(String json) {
    return JsonBaseModel.fromJsonString(json, LiveRequest.class);
}
@JsonCreator                        // <-- BUG: outside Builder; Jackson can't find a creator for Builder
static LiveRequest.Builder jacksonBuilder() {
    return LiveRequest.builder();
}

Every other model in the codebase places @JsonCreator jacksonBuilder() inside its Builder class (e.g. LlmResponse.java:114-119, StdioServerParameters, MemoryEntry, EventCompaction); LiveRequest is the lone outlier. (Note: the dev websocket handler reconstructs LiveRequest manually from a parsed JsonNode rather than calling fromJsonString, consistent with this API being unusable.)

Steps to Reproduce

LiveRequest req = LiveRequest.builder()
    .content(Content.builder().parts(Part.fromText("hi")).build())
    .close(false).build();
String json = req.toJson();                              // OK
LiveRequest back = JsonBaseModel.fromJsonString(json, LiveRequest.class);  // throws

Even minimal {"close":true} fails.

Expected Behavior

fromJsonString deserializes any valid LiveRequest JSON, including the output of toJson() (round-trip).

Observed Behavior

IllegalStateException (wrapping)
  Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
  Cannot construct instance of `com.google.adk.agents.LiveRequest$Builder`
  (no Creators, like default constructor, exist): cannot deserialize from Object value

Suggested Fix

Move jacksonBuilder() inside the Builder class, matching the rest of the codebase:

public abstract static class Builder {
    ...
    @JsonCreator
    static LiveRequest.Builder jacksonBuilder() { return LiveRequest.builder(); }
    public final LiveRequest build() { ... }
}

Corresponding Test (generated)

@Test
public void testLiveRequestToJsonRoundTrip() {
    // Given
    Content content = Content.builder()
        .parts(Part.fromText("Test content"))
        .build();
    
    LiveRequest liveRequest = LiveRequest.builder()
        .content(content)
        .close(false)
        .build();

    // When
    String json = liveRequest.toJson();
    // Since LiveRequest has a complex builder pattern and Jackson struggles with it,
    // we'll test the serialization/deserialization using JsonBaseModel directly
    // but verify that the JSON can be parsed back into the same object type
    LiveRequest deserialized = JsonBaseModel.fromJsonString(json, LiveRequest.class);

    // Then
    assertNotNull(json);
    assertNotNull(deserialized);
    assertTrue(deserialized.content().isPresent());
    assertEquals(content, deserialized.content().get());
    assertFalse(deserialized.shouldClose());
}

Environment

  • Project: adk / google-adk (com.google.adk) · upstream: github.com/google/adk-java
  • Version: 0.5.1-SNAPSHOT (local) · Commit: <fill SUT commit>
  • Java 17 · OS macOS

This input was generated by the test case generator TestFusion developed in our STAR lab.

Metadata

Metadata

Assignees

Labels

waiting on reporterWaiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale.

Type

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions