Skip to content

fix(client): throw DoclingServeClientException for a 422 without validation details - #714

Merged
edeandrea merged 1 commit into
docling-project:mainfrom
edeandrea:fix/710-422-without-validation-details
Sep 29, 2026
Merged

edeandrea merged 1 commit into
docling-project:mainfrom
edeandrea:fix/710-422-without-validation-details

Conversation

@edeandrea

Copy link
Copy Markdown
Contributor

Summary

DoclingServeClient.getResponse treated every 422 as a validation error. ValidationError is deserialized leniently (unknown properties are ignored), so any JSON object without validation details, such as {}, {"error":"gateway says no"} or {"detail":[]}, became a ValidationError with an empty errorDetails list. The client then threw a ValidationException with a blank message, and the response body was not available to the caller.

A ValidationError without details is now treated as "not a validation error", and the client throws the generic DoclingServeClientException, which carries the status code and the response body, like any other 4xx/5xx response. A 422 that carries validation details still throws a ValidationException.

Closes #710

Changes

  • DoclingServeClient.getResponse: filter out a ValidationError with no details and fall through to DoclingServeClientException.
  • AbstractDoclingServeClientTests.UnprocessableEntityResponseTests (runs on both the Jackson 2 and Jackson 3 clients): a parameterized test for {"error":"..."}, {} and {"detail":[]}, and a test that a body with details is still a ValidationException. Removing the filter turns the three no-details cases red on both backends.
  • serve-api.md ("Validation errors") and whats-new.md.

Notes

  • Behavior change for {"detail": []}: it goes from ValidationException to DoclingServeClientException. I have not confirmed whether docling-serve can ever emit an empty detail list. An empty ValidationException has nothing to inspect and no message, while the generic exception keeps the status and the body, so I think this is the better result either way. Please push back if you know otherwise.
  • Relation to fix(client): keep status and body when a 422 body is not JSON #704: fix(client): keep status and body when a 422 body is not JSON #704 restructures this branch into parseValidationError. The filter is written inline here because that method does not exist on main; whichever PR lands second has a small conflict to resolve in this block, and the filter then becomes a one-line .filter(...) on the Optional.
  • Not covered: with Jackson 2, {"detail":null} on a 422 throws a JsonReadException ("errorDetails cannot be null") instead of falling back. That is a parse failure on the same path, so it fits better with fix(client): keep status and body when a 422 body is not JSON #704 or a follow-up. I have not checked Jackson 3.

…dation details

A 422 response whose body is a JSON object without validation details (for example {}, {"error":"..."} or {"detail":[]}) was deserialized into an empty ValidationError and thrown as a ValidationException with a blank message, losing the response body. Treat a ValidationError without details as not being a validation error and throw the generic DoclingServeClientException, which keeps the status code and the response body.

Closes docling-project#710

Signed-off-by: Eric Deandrea <eric.deandrea@ibm.com>
@edeandrea edeandrea added bug Something isn't working module:docling-serve-client The docling-serve-client module labels Sep 29, 2026
@edeandrea
edeandrea enabled auto-merge (squash) September 29, 2026 20:56
@github-actions

Copy link
Copy Markdown

:java_duke: JaCoCo coverage report

Overall Project 49.73% 🔴

There is no coverage information present for the Files changed

@github-actions

Copy link
Copy Markdown
TestsPassed ✅SkippedFailed
Gradle Test Results (all modules & JDKs)2188 ran2188 passed0 skipped0 failed
TestResult
No test annotations available

@github-actions

Copy link
Copy Markdown

HTML test reports are available as workflow artifacts (zipped HTML).

• Download: Artifacts for this run

@edeandrea
edeandrea merged commit 8b77e2a into docling-project:main Sep 29, 2026
29 checks passed
Ashfaqbs added a commit to Ashfaqbs/docling-java that referenced this pull request Sep 30, 2026
A 422 whose body cannot be parsed as a validation error (for example an
HTML page from a gateway) surfaced as a raw Jackson parse exception and
lost the status code and response body. Fall back to
DoclingServeClientException, as for any other 4xx/5xx.

Rebased onto main after docling-project#711 and docling-project#714:
- Narrow the fallback catch to the new JsonReadException instead of a
  broad RuntimeException, so a failure that is not a parse failure (a
  broken custom deserializer) still propagates.
- parseValidationError now returns Optional<ValidationError> instead of
  a @nullable, folded together with docling-project#714's no-details filter.
- Move the standalone tests into AbstractDoclingServeClientTests'
  UnprocessableEntityResponseTests, so both Jackson backends run them,
  and add a case guarding the narrowed catch via a mixin-based failing
  deserializer for ValidationError.
- Update serve-api.md and whats-new.md.

Signed-off-by: Ashfaqbs <105435085+Ashfaqbs@users.noreply.github.com>
edeandrea pushed a commit to Ashfaqbs/docling-java that referenced this pull request Oct 1, 2026
A 422 whose body cannot be parsed as a validation error (for example an
HTML page from a gateway) surfaced as a raw Jackson parse exception and
lost the status code and response body. Fall back to
DoclingServeClientException, as for any other 4xx/5xx.

Rebased onto main after docling-project#711 and docling-project#714:
- Narrow the fallback catch to the new JsonReadException instead of a
  broad RuntimeException, so a failure that is not a parse failure (a
  broken custom deserializer) still propagates.
- parseValidationError now returns Optional<ValidationError> instead of
  a @nullable, folded together with docling-project#714's no-details filter.
- Move the standalone tests into AbstractDoclingServeClientTests'
  UnprocessableEntityResponseTests, so both Jackson backends run them,
  and add a case guarding the narrowed catch via a mixin-based failing
  deserializer for ValidationError.
- Update serve-api.md and whats-new.md.

Signed-off-by: Ashfaqbs <105435085+Ashfaqbs@users.noreply.github.com>
edeandrea pushed a commit to Ashfaqbs/docling-java that referenced this pull request Oct 1, 2026
A 422 whose body cannot be parsed as a validation error (for example an
HTML page from a gateway) surfaced as a raw Jackson parse exception and
lost the status code and response body. Fall back to
DoclingServeClientException, as for any other 4xx/5xx.

Rebased onto main after docling-project#711 and docling-project#714:
- Narrow the fallback catch to the new JsonReadException instead of a
  broad RuntimeException, so a failure that is not a parse failure (a
  broken custom deserializer) still propagates.
- parseValidationError now returns Optional<ValidationError> instead of
  a @nullable, folded together with docling-project#714's no-details filter.
- Move the standalone tests into AbstractDoclingServeClientTests'
  UnprocessableEntityResponseTests, so both Jackson backends run them,
  and add a case guarding the narrowed catch via a mixin-based failing
  deserializer for ValidationError.
- Update serve-api.md and whats-new.md.

Signed-off-by: Ashfaqbs <105435085+Ashfaqbs@users.noreply.github.com>
edeandrea pushed a commit to Ashfaqbs/docling-java that referenced this pull request Oct 2, 2026
A 422 whose body cannot be parsed as a validation error (for example an
HTML page from a gateway) surfaced as a raw Jackson parse exception and
lost the status code and response body. Fall back to
DoclingServeClientException, as for any other 4xx/5xx.

Rebased onto main after docling-project#711 and docling-project#714:
- Narrow the fallback catch to the new JsonReadException instead of a
  broad RuntimeException, so a failure that is not a parse failure (a
  broken custom deserializer) still propagates.
- parseValidationError now returns Optional<ValidationError> instead of
  a @nullable, folded together with docling-project#714's no-details filter.
- Move the standalone tests into AbstractDoclingServeClientTests'
  UnprocessableEntityResponseTests, so both Jackson backends run them,
  and add a case guarding the narrowed catch via a mixin-based failing
  deserializer for ValidationError.
- Update serve-api.md and whats-new.md.

Signed-off-by: Ashfaqbs <105435085+Ashfaqbs@users.noreply.github.com>
edeandrea pushed a commit to Ashfaqbs/docling-java that referenced this pull request Oct 5, 2026
A 422 whose body cannot be parsed as a validation error (for example an
HTML page from a gateway) surfaced as a raw Jackson parse exception and
lost the status code and response body. Fall back to
DoclingServeClientException, as for any other 4xx/5xx.

Rebased onto main after docling-project#711 and docling-project#714:
- Narrow the fallback catch to the new JsonReadException instead of a
  broad RuntimeException, so a failure that is not a parse failure (a
  broken custom deserializer) still propagates.
- parseValidationError now returns Optional<ValidationError> instead of
  a @nullable, folded together with docling-project#714's no-details filter.
- Move the standalone tests into AbstractDoclingServeClientTests'
  UnprocessableEntityResponseTests, so both Jackson backends run them,
  and add a case guarding the narrowed catch via a mixin-based failing
  deserializer for ValidationError.
- Update serve-api.md and whats-new.md.

Signed-off-by: Ashfaqbs <105435085+Ashfaqbs@users.noreply.github.com>
edeandrea pushed a commit that referenced this pull request Oct 5, 2026
* fix(client): keep status and body when a 422 is not a validation error

A 422 whose body cannot be parsed as a validation error (for example an
HTML page from a gateway) surfaced as a raw Jackson parse exception and
lost the status code and response body. Fall back to
DoclingServeClientException, as for any other 4xx/5xx.

Rebased onto main after #711 and #714:
- Narrow the fallback catch to the new JsonReadException instead of a
  broad RuntimeException, so a failure that is not a parse failure (a
  broken custom deserializer) still propagates.
- parseValidationError now returns Optional<ValidationError> instead of
  a @nullable, folded together with #714's no-details filter.
- Move the standalone tests into AbstractDoclingServeClientTests'
  UnprocessableEntityResponseTests, so both Jackson backends run them,
  and add a case guarding the narrowed catch via a mixin-based failing
  deserializer for ValidationError.
- Update serve-api.md and whats-new.md.

Signed-off-by: Ashfaqbs <105435085+Ashfaqbs@users.noreply.github.com>

* fix(client): use Optional map/orElseGet chain and guard null detail bodies

Addresses edeandrea's remaining review notes on #704: getResponse now
throws validationError.map(...).orElseGet(...) instead of isPresent()/
get(), parenthesizes the ternary condition, and computes body.toString()
once as responseBody. Also adds "null" and {"detail":null} cases to
jsonBodyWithoutValidationDetailsKeepsStatusAndBody, which guard the
Optional.ofNullable in parseValidationError against regressing to an
unnoticed NPE.

Signed-off-by: Ashfaq <105435085+Ashfaqbs@users.noreply.github.com>

---------

Signed-off-by: Ashfaqbs <105435085+Ashfaqbs@users.noreply.github.com>
Signed-off-by: Ashfaq <105435085+Ashfaqbs@users.noreply.github.com>
@docling-java-ops docling-java-ops Bot added the released Issue has been released label Oct 5, 2026
@docling-java-ops

Copy link
Copy Markdown
Contributor

🎉 This issue has been resolved in v0.7.0 (Release Notes)

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

Labels

bug Something isn't working module:docling-serve-client The docling-serve-client module released Issue has been released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

422 with a JSON body that has no validation details throws ValidationException with a blank message

1 participant