From dbb84c52123e4bb99545a76ffb2dbc680fe47941 Mon Sep 17 00:00:00 2001 From: Marcellus Tavares Date: Fri, 29 May 2026 16:48:12 -0700 Subject: [PATCH 1/2] [Cosmos] Fix ClassCastException when building CosmosException from an empty error response body --- .../implementation/JsonSerializableTest.java | 20 +++++++++++++++++++ .../implementation/JsonSerializable.java | 18 ++++++++++++++--- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/JsonSerializableTest.java b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/JsonSerializableTest.java index b0abf602b44b..149d3cfd8aaa 100644 --- a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/JsonSerializableTest.java +++ b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/JsonSerializableTest.java @@ -11,6 +11,8 @@ import org.testng.annotations.Test; import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; @@ -34,4 +36,22 @@ public void instantiateWithObjectNodeAndType() throws IOException { assertThat(jsonSerializable).isInstanceOf(klass); } } + + @Test(groups = {"unit"}) + public void instantiateFromNonObjectJsonReturnsEmptyObject() { + for (String body : Arrays.asList("", " ", "null", "[]", "123")) { + JsonSerializable jsonSerializable = new JsonSerializable(body); + assertThat(jsonSerializable.propertyBag).isNotNull(); + assertThat(jsonSerializable.propertyBag.isEmpty()).isTrue(); + } + } + + @Test(groups = {"unit"}) + public void instantiateFromNonObjectJsonBytesReturnsEmptyObject() { + for (String body : Arrays.asList("", "null", "[]", "123")) { + byte[] bytes = body.getBytes(StandardCharsets.UTF_8); + assertThat(new JsonSerializable(bytes).propertyBag.isEmpty()).isTrue(); + assertThat(new JsonSerializable(ByteBuffer.wrap(bytes)).propertyBag.isEmpty()).isTrue(); + } + } } diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/JsonSerializable.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/JsonSerializable.java index b955af019334..b688e10091b9 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/JsonSerializable.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/JsonSerializable.java @@ -606,7 +606,11 @@ public Object getObjectByPath(List propertyNames) { private ObjectNode fromJson(byte[] bytes) { try { - return (ObjectNode) OBJECT_MAPPER.readTree(bytes); + JsonNode jsonNode = OBJECT_MAPPER.readTree(bytes); + if (jsonNode instanceof ObjectNode) { + return (ObjectNode) jsonNode; + } + return OBJECT_MAPPER.createObjectNode(); } catch (IOException e) { throw new IllegalArgumentException( String.format("Unable to parse JSON %s", Arrays.toString(bytes)), e); @@ -615,7 +619,11 @@ private ObjectNode fromJson(byte[] bytes) { private ObjectNode fromJson(String json, ObjectMapper objectMapper) { try { - return (ObjectNode) objectMapper.readTree(json); + JsonNode jsonNode = objectMapper.readTree(json); + if (jsonNode instanceof ObjectNode) { + return (ObjectNode) jsonNode; + } + return objectMapper.createObjectNode(); } catch (IOException e) { throw new IllegalArgumentException( String.format("Unable to parse JSON %s", json), e); @@ -624,7 +632,11 @@ private ObjectNode fromJson(String json, ObjectMapper objectMapper) { private ObjectNode fromJson(ByteBuffer json) { try { - return (ObjectNode) OBJECT_MAPPER.readTree(new ByteBufferBackedInputStream(json)); + JsonNode jsonNode = OBJECT_MAPPER.readTree(new ByteBufferBackedInputStream(json)); + if (jsonNode instanceof ObjectNode) { + return (ObjectNode) jsonNode; + } + return OBJECT_MAPPER.createObjectNode(); } catch (IOException e) { throw new IllegalArgumentException("Unable to parse JSON from ByteBuffer", e); } From cd25ad50ae55a115546be573516933a58532320a Mon Sep 17 00:00:00 2001 From: Marcellus Tavares Date: Fri, 29 May 2026 16:56:23 -0700 Subject: [PATCH 2/2] changelog --- sdk/cosmos/azure-cosmos/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/cosmos/azure-cosmos/CHANGELOG.md b/sdk/cosmos/azure-cosmos/CHANGELOG.md index 4d6c3781b00b..032ba2595b22 100644 --- a/sdk/cosmos/azure-cosmos/CHANGELOG.md +++ b/sdk/cosmos/azure-cosmos/CHANGELOG.md @@ -12,6 +12,7 @@ #### Other Changes * Replaced per-client `Schedulers.newSingle()` schedulers in `GlobalEndpointManager` and `GlobalPartitionEndpointManagerForPerPartitionCircuitBreaker` with shared `BoundedElastic` schedulers in `CosmosSchedulers` to prevent thread count from scaling linearly with client/tenant count. - See [PR 49062](https://github.com/Azure/azure-sdk-for-java/pull/49062) * Fixed a sporadic `NullPointerException` in `JsonSerializable.getWithMapping` triggered by concurrent first-time calls to `DatabaseAccount.getConsistencyPolicy()` and its sibling lazy getters (`getReplicationPolicy`, `getSystemReplicationPolicy`, `getQueryEngineConfiguration`). The fix makes `JsonSerializable.propertyBag` `final`, closing an unsafe-publication race in the lazy-initialisation pattern. - See [Issue 49256](https://github.com/Azure/azure-sdk-for-java/issues/49256) and [PR #49258](https://github.com/Azure/azure-sdk-for-java/pull/49258) +* Fixed a `ClassCastException` (`MissingNode cannot be cast to ObjectNode`) thrown by `JsonSerializable` when parsing an empty, blank or non-object payload. Non-object payloads are now treated as an empty object across the `String`, `byte[]` and `ByteBuffer` parsing paths. - See [Issue 49322](https://github.com/Azure/azure-sdk-for-java/issues/49322) ### 4.80.0 (2026-05-01)