Describe the bug
JsonSerializable.fromJson(String, ObjectMapper) performs an unchecked cast of the parsed tree to ObjectNode:
return (ObjectNode) objectMapper.readTree(json);
When json is empty or blank, ObjectMapper.readTree(...) returns a MissingNode (not an ObjectNode), so the cast throws ClassCastException: class ...MissingNode cannot be cast to class ...ObjectNode.
This is reached on the error-handling path: when a request gets an error HTTP response with an empty body, HttpClientUtils.createDocumentClientException builds the exception from the body, defaulting an empty body to StringUtils.EMPTY and then calling new CosmosError(body):
|
Mono<String> readStream = httpResponse.bodyAsString().switchIfEmpty(Mono.just(StringUtils.EMPTY)); |
|
|
|
return readStream.map(body -> { |
|
CosmosError cosmosError = new CosmosError(body); |
new CosmosError(String) → JsonSerializable(String) → fromJson(...):
|
public CosmosError(String jsonString) { |
|
public JsonSerializable(String jsonString) { |
|
return (ObjectNode) objectMapper.readTree(json); |
Exception or Stack Trace
Observed in production on azure-cosmos:4.80.0 (manifests as a CosmosException with statusCode 0):
Caused by: java.lang.ClassCastException: class com.fasterxml.jackson.databind.node.MissingNode cannot be cast to class com.fasterxml.jackson.databind.node.ObjectNode
at com.azure.cosmos.implementation.JsonSerializable.fromJson(JsonSerializable.java:618)
at com.azure.cosmos.implementation.JsonSerializable.<init>(JsonSerializable.java:93)
at com.azure.cosmos.implementation.CosmosError.<init>(CosmosError.java:35)
at com.azure.cosmos.implementation.directconnectivity.HttpClientUtils.lambda$createDocumentClientException$2(HttpClientUtils.java:45)
at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:106)
... 58 more
The enclosing CosmosException is created here, carrying statusCode 0 and an empty responseHeaders {}:
at com.azure.cosmos.BridgeInternal.createCosmosException(BridgeInternal.java:416)
at com.azure.cosmos.implementation.directconnectivity.GatewayAddressCache.lambda$getServerAddressesViaGatewayInternalAsync$11(GatewayAddressCache.java:514)
To Reproduce
The crash happens inside the SDK's own exception construction, so it can be reproduced deterministically by exercising the exact call the SDK makes for an empty error body — new CosmosError(<empty/blank string>):
- Add
com.azure:azure-cosmos:4.80.0 to the classpath.
- Construct a
CosmosError from an empty (or whitespace-only) body, as HttpClientUtils.createDocumentClientException does when an error response has no body.
- Observe
ClassCastException instead of a usable CosmosError.
(In a live service this occurs when a Gateway/server error response is returned with an empty body — e.g. transient connectivity errors during address resolution.)
Code Snippet
Minimal reproduction (throws on 4.80.0):
import com.azure.cosmos.implementation.CosmosError;
// Both throw: java.lang.ClassCastException: MissingNode cannot be cast to ObjectNode
new CosmosError("");
new CosmosError(" ");
// Control: a valid JSON object works fine
new CosmosError("{}"); // OK
Proposed regression test (TestNG, unit group), alongside the existing JsonSerializableTest:
@Test(groups = {"unit"})
public void instantiateFromEmptyJsonStringDoesNotThrow() {
// An error HTTP response with an empty/blank body becomes a CosmosError via
// HttpClientUtils.createDocumentClientException -> new CosmosError(body).
for (String emptyBody : Arrays.asList("", " ")) {
CosmosError error = new CosmosError(emptyBody);
assertThat(error).isNotNull();
assertThat(error.getCode()).isNull();
assertThat(error.getMessage()).isNull();
}
}
Expected behavior
Constructing a CosmosError/JsonSerializable from an empty or blank body should not throw ClassCastException. An empty body should yield an empty error object.
Screenshots
N/A
Setup (please complete the following information):
N/A
Additional context
N/a
Information Checklist
Describe the bug
JsonSerializable.fromJson(String, ObjectMapper)performs an unchecked cast of the parsed tree toObjectNode:When
jsonis empty or blank,ObjectMapper.readTree(...)returns aMissingNode(not anObjectNode), so the cast throwsClassCastException: class ...MissingNode cannot be cast to class ...ObjectNode.This is reached on the error-handling path: when a request gets an error HTTP response with an empty body,
HttpClientUtils.createDocumentClientExceptionbuilds the exception from the body, defaulting an empty body toStringUtils.EMPTYand then callingnew CosmosError(body):azure-sdk-for-java/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/directconnectivity/HttpClientUtils.java
Lines 42 to 45 in 57ee419
new CosmosError(String)→JsonSerializable(String)→fromJson(...):azure-sdk-for-java/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/CosmosError.java
Line 34 in 57ee419
azure-sdk-for-java/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/JsonSerializable.java
Line 92 in 57ee419
azure-sdk-for-java/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/JsonSerializable.java
Line 618 in 57ee419
Exception or Stack Trace
Observed in production on
azure-cosmos:4.80.0(manifests as aCosmosExceptionwithstatusCode 0):The enclosing
CosmosExceptionis created here, carryingstatusCode 0and an emptyresponseHeaders {}:To Reproduce
The crash happens inside the SDK's own exception construction, so it can be reproduced deterministically by exercising the exact call the SDK makes for an empty error body —
new CosmosError(<empty/blank string>):com.azure:azure-cosmos:4.80.0to the classpath.CosmosErrorfrom an empty (or whitespace-only) body, asHttpClientUtils.createDocumentClientExceptiondoes when an error response has no body.ClassCastExceptioninstead of a usableCosmosError.(In a live service this occurs when a Gateway/server error response is returned with an empty body — e.g. transient connectivity errors during address resolution.)
Code Snippet
Minimal reproduction (throws on 4.80.0):
Proposed regression test (TestNG,
unitgroup), alongside the existingJsonSerializableTest:Expected behavior
Constructing a
CosmosError/JsonSerializablefrom an empty or blank body should not throwClassCastException. An empty body should yield an empty error object.Screenshots
N/A
Setup (please complete the following information):
N/A
Additional context
N/a
Information Checklist