Skip to content

logResponse throws on non-JSON response bodies when prettyPrint is enabled, hiding the real error #709

Description

@edeandrea

Summary

When logResponses() and prettyPrint() are both enabled, DoclingServeClient.logResponse(...) tries to parse every response body as JSON so it can pretty-print it. If the body is not JSON (for example an HTML error page from a gateway or proxy in front of docling-serve), that parse throws, and the exception replaces the real error. The caller gets a raw Jackson exception instead of a DoclingServeClientException, and the HTTP status code and response body are lost.

Reproduction

Tested on main (174a56e) with a WireMock stub for GET /health, on both DoclingServeJackson2Client and DoclingServeJackson3Client, with logResponses() enabled:

Status Content-Type Body prettyPrint() Result
502 text/html <html>Bad Gateway</html> off DoclingServeClientException (status 502, body kept)
502 text/html <html>Bad Gateway</html> on Jackson 2: RuntimeException wrapping JsonParseException; Jackson 3: StreamReadException
500 text/plain Internal Server Error on same as above
422 text/html <html>x</html> on same as above

So this is not specific to 422: any non-JSON response body fails this way when both options are on.

Cause

logResponse runs from getResponse before the status code is checked:

responseBody
    .map(body -> this.config.prettyPrint() ? writeValueAsString(readValue(body, Object.class)) : body)

readValue(body, Object.class) throws for a body that is not valid JSON, and nothing catches it.

Suggested fix

Make the pretty-printing step best-effort: if the body cannot be parsed and re-serialized, log the raw body instead. A broad catch (RuntimeException) is appropriate here. It is only formatting for a log line, so it has no reason to tell parse failures from other failures, and the base class cannot name the Jackson exception types anyway (only one Jackson version may be on the classpath).

Logging must never change the outcome of the request.

Tests

The shared clients in AbstractDoclingServeClientTests are built with .logRequests().logResponses().prettyPrint(), so a regression test belongs there and will run against both Jackson backends: a WireMock stub returning 502 with an HTML body, asserting a DoclingServeClientException with status 502 and the original body.

Related

Found while reviewing #704, which adds a fallback to DoclingServeClientException for a 422 whose body is not a validation error. That fallback is bypassed for clients with both options enabled, because this code throws first.

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingmodule:docling-serve-clientThe docling-serve-client modulereleasedIssue has been released

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions