diff --git a/components/abstractions/spotBugsExcludeFilter.xml b/components/abstractions/spotBugsExcludeFilter.xml
index 8c33623ae..cf787f100 100644
--- a/components/abstractions/spotBugsExcludeFilter.xml
+++ b/components/abstractions/spotBugsExcludeFilter.xml
@@ -80,4 +80,10 @@ xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubu
+
+
+
+
+
+
diff --git a/components/abstractions/src/main/java/com/microsoft/kiota/ClientException.java b/components/abstractions/src/main/java/com/microsoft/kiota/ClientException.java
new file mode 100644
index 000000000..2be6368bd
--- /dev/null
+++ b/components/abstractions/src/main/java/com/microsoft/kiota/ClientException.java
@@ -0,0 +1,28 @@
+package com.microsoft.kiota;
+
+import jakarta.annotation.Nonnull;
+
+/** An error in client-side request or response processing, rather than an API error response. */
+public class ClientException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ /** {@inheritDoc} */
+ public ClientException() {
+ super();
+ }
+
+ /** {@inheritDoc} */
+ public ClientException(@Nonnull final String message) {
+ super(message);
+ }
+
+ /** {@inheritDoc} */
+ public ClientException(@Nonnull final String message, @Nonnull final Throwable cause) {
+ super(message, cause);
+ }
+
+ /** {@inheritDoc} */
+ public ClientException(@Nonnull final Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/components/abstractions/src/main/java/com/microsoft/kiota/serialization/ParseNodeFactoryRegistry.java b/components/abstractions/src/main/java/com/microsoft/kiota/serialization/ParseNodeFactoryRegistry.java
index 61e06c997..84f1b3d71 100644
--- a/components/abstractions/src/main/java/com/microsoft/kiota/serialization/ParseNodeFactoryRegistry.java
+++ b/components/abstractions/src/main/java/com/microsoft/kiota/serialization/ParseNodeFactoryRegistry.java
@@ -1,5 +1,7 @@
package com.microsoft.kiota.serialization;
+import com.microsoft.kiota.ClientException;
+
import jakarta.annotation.Nonnull;
import java.io.InputStream;
@@ -32,6 +34,9 @@ public ParseNodeFactoryRegistry() {
private static final Pattern contentTypeVendorCleanupPattern =
Pattern.compile("[^/]+\\+", Pattern.CASE_INSENSITIVE);
+ /** {@inheritDoc}
+ * @throws ClientException when no factory is registered for the content type
+ */
@Override
@Nonnull public ParseNode getParseNode(
@Nonnull final String contentType, @Nonnull final InputStream rawResponse) {
@@ -53,7 +58,7 @@ public ParseNodeFactoryRegistry() {
.get(cleanedContentType)
.getParseNode(cleanedContentType, rawResponse);
}
- throw new RuntimeException(
+ throw new ClientException(
"Content type " + cleanedContentType + " does not have a factory to be parsed");
}
}
diff --git a/components/abstractions/src/main/java/com/microsoft/kiota/serialization/SerializationWriterFactoryRegistry.java b/components/abstractions/src/main/java/com/microsoft/kiota/serialization/SerializationWriterFactoryRegistry.java
index 9fcb2cb23..af7cb3a97 100644
--- a/components/abstractions/src/main/java/com/microsoft/kiota/serialization/SerializationWriterFactoryRegistry.java
+++ b/components/abstractions/src/main/java/com/microsoft/kiota/serialization/SerializationWriterFactoryRegistry.java
@@ -1,5 +1,6 @@
package com.microsoft.kiota.serialization;
+import com.microsoft.kiota.ClientException;
import com.microsoft.kiota.store.BackingStoreSerializationWriterProxyFactory;
import jakarta.annotation.Nonnull;
@@ -43,7 +44,7 @@ public SerializationWriterFactoryRegistry() {
* @param contentType
* @param serializeOnlyChangedValues control backing store functionality
* @return the serialization writer
- * @throws RuntimeException when no factory is found for content type
+ * @throws ClientException when no factory is found for content type
*/
@Nonnull public SerializationWriter getSerializationWriter(
@Nonnull final String contentType, final boolean serializeOnlyChangedValues) {
@@ -59,7 +60,7 @@ public SerializationWriterFactoryRegistry() {
getSerializationWriterFactory(
getCleanedVendorSpecificContentType(cleanedContentType));
if (factory == null) {
- throw new RuntimeException(
+ throw new ClientException(
"Content type "
+ contentType
+ " does not have a factory to be serialized");
diff --git a/components/abstractions/src/test/java/com/microsoft/kiota/serialization/FactoryRegistryExceptionTest.java b/components/abstractions/src/test/java/com/microsoft/kiota/serialization/FactoryRegistryExceptionTest.java
new file mode 100644
index 000000000..bd00b5ec4
--- /dev/null
+++ b/components/abstractions/src/test/java/com/microsoft/kiota/serialization/FactoryRegistryExceptionTest.java
@@ -0,0 +1,80 @@
+package com.microsoft.kiota.serialization;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import com.microsoft.kiota.ClientException;
+import com.microsoft.kiota.Compatibility;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+
+class FactoryRegistryExceptionTest {
+ @Test
+ void unsupportedResponseContentTypeThrowsClientException() {
+ var registry = new ParseNodeFactoryRegistry();
+ var response =
+ new ByteArrayInputStream(
+ "Unavailable".getBytes(StandardCharsets.UTF_8));
+ var error =
+ assertThrows(
+ ClientException.class,
+ () -> registry.getParseNode("text/html; charset=utf-8", response));
+ assertEquals(
+ "Content type text/html does not have a factory to be parsed", error.getMessage());
+ }
+
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ void unsupportedRequestContentTypeThrowsClientException(boolean changedValuesOnly) {
+ var registry = new SerializationWriterFactoryRegistry();
+ var error =
+ assertThrows(
+ ClientException.class,
+ () -> registry.getSerializationWriter("text/html", changedValuesOnly));
+ assertEquals(
+ "Content type text/html does not have a factory to be serialized",
+ error.getMessage());
+ }
+
+ @Test
+ void invalidArgumentsKeepTheirValidationExceptions() {
+ var parser = new ParseNodeFactoryRegistry();
+ var writer = new SerializationWriterFactoryRegistry();
+ assertThrows(
+ NullPointerException.class,
+ () -> parser.getParseNode(null, new ByteArrayInputStream(new byte[0])));
+ assertThrows(
+ NullPointerException.class,
+ () -> parser.getParseNode("", new ByteArrayInputStream(new byte[0])));
+ assertThrows(NullPointerException.class, () -> parser.getParseNode("text/html", null));
+ assertThrows(NullPointerException.class, () -> writer.getSerializationWriter(null));
+ assertThrows(NullPointerException.class, () -> writer.getSerializationWriter(""));
+ }
+
+ @Test
+ void registeredVendorContentTypesStillWork() throws Exception {
+ var parser = new ParseNodeFactoryRegistry();
+ parser.contentTypeAssociatedFactories.put("application/json", new JsonParseNodeFactory());
+ var response = new ByteArrayInputStream("42".getBytes(StandardCharsets.UTF_8));
+ assertEquals(
+ 42,
+ parser.getParseNode("application/vnd.example+json; charset=utf-8", response)
+ .getIntegerValue());
+ var writers = new SerializationWriterFactoryRegistry();
+ writers.contentTypeAssociatedFactories.put(
+ "application/json", new JsonSerializationWriterFactory());
+ try (var writer = writers.getSerializationWriter("application/vnd.example+json")) {
+ writer.writeIntegerValue(null, 42);
+ assertEquals(
+ "42",
+ new String(
+ Compatibility.readAllBytes(writer.getSerializedContent()),
+ StandardCharsets.UTF_8));
+ }
+ }
+}