This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch CAMEL-26509-type-conversion-from-type
in repository https://gitbox.apache.org/repos/asf/camel.git

commit eb275279a5484235320694158e544c45448d5a6d
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Nov 3 15:45:33 2026 +0100

    CAMEL-26509: camel-core - TypeConversionException gives the real from type, 
and ArrayTypeConverter gives no conversion for an element that does not fit
    
    When a converter failed during the conversion of a message, the registry 
built the
    TypeConversionException from the value prepared for logging, which is a 
String. So the
    message always said "from type: java.lang.String", and 
getValue()/getFromType() gave that
    String. The message no longer includes the value (CAMEL-24553), so the 
exception now gets
    the value itself.
    
    ArrayTypeConverter filled the target array with Array.set and 
Collection.toArray, which
    throw IllegalArgumentException (argument type mismatch) or 
ArrayStoreException when an
    element does not fit the component type, such as a list of strings to int[] 
or byte[].
    It now returns null (no conversion) so other converters can try.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../camel/impl/converter/ArrayTypeConverter.java   | 37 ++++++-----
 .../impl/converter/CoreTypeConverterRegistry.java  | 12 +---
 .../converter/CoreTypeConverterRegistryTest.java   | 71 ++++++++++++++++++++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    | 12 ++++
 4 files changed, 106 insertions(+), 26 deletions(-)

diff --git 
a/core/camel-base/src/main/java/org/apache/camel/impl/converter/ArrayTypeConverter.java
 
b/core/camel-base/src/main/java/org/apache/camel/impl/converter/ArrayTypeConverter.java
index b053bed4ff52..ce1f73048bce 100644
--- 
a/core/camel-base/src/main/java/org/apache/camel/impl/converter/ArrayTypeConverter.java
+++ 
b/core/camel-base/src/main/java/org/apache/camel/impl/converter/ArrayTypeConverter.java
@@ -35,24 +35,29 @@ public class ArrayTypeConverter extends 
TypeConverterSupport {
     @SuppressWarnings("unchecked")
     public <T> T convertTo(Class<T> type, Exchange exchange, Object value) {
         if (type.isArray()) {
-            if (value instanceof Collection collection) {
-                Object array = Array.newInstance(type.getComponentType(), 
collection.size());
-                if (array instanceof Object[]) {
-                    collection.toArray((Object[]) array);
-                } else {
-                    int index = 0;
-                    for (Object element : collection) {
-                        Array.set(array, index++, element);
+            // an element that does not fit the component type of the array 
cannot be converted
+            try {
+                if (value instanceof Collection collection) {
+                    Object array = Array.newInstance(type.getComponentType(), 
collection.size());
+                    if (array instanceof Object[]) {
+                        collection.toArray((Object[]) array);
+                    } else {
+                        int index = 0;
+                        for (Object element : collection) {
+                            Array.set(array, index++, element);
+                        }
                     }
+                    return (T) array;
+                } else if (value != null && value.getClass().isArray()) {
+                    int size = Array.getLength(value);
+                    Object answer = Array.newInstance(type.getComponentType(), 
size);
+                    for (int i = 0; i < size; i++) {
+                        Array.set(answer, i, Array.get(value, i));
+                    }
+                    return (T) answer;
                 }
-                return (T) array;
-            } else if (value != null && value.getClass().isArray()) {
-                int size = Array.getLength(value);
-                Object answer = Array.newInstance(type.getComponentType(), 
size);
-                for (int i = 0; i < size; i++) {
-                    Array.set(answer, i, Array.get(value, i));
-                }
-                return (T) answer;
+            } catch (IllegalArgumentException | ArrayStoreException e) {
+                return null;
             }
         } else if (Collection.class.isAssignableFrom(type) && 
type.isAssignableFrom(ArrayList.class)) {
             // only for collection types a list can be assigned to (such as 
not a Set)
diff --git 
a/core/camel-base/src/main/java/org/apache/camel/impl/converter/CoreTypeConverterRegistry.java
 
b/core/camel-base/src/main/java/org/apache/camel/impl/converter/CoreTypeConverterRegistry.java
index 53a2d7180665..e81419dd5eb8 100644
--- 
a/core/camel-base/src/main/java/org/apache/camel/impl/converter/CoreTypeConverterRegistry.java
+++ 
b/core/camel-base/src/main/java/org/apache/camel/impl/converter/CoreTypeConverterRegistry.java
@@ -40,7 +40,6 @@ import org.apache.camel.spi.CamelLogger;
 import org.apache.camel.spi.Injector;
 import org.apache.camel.spi.TypeConverterRegistry;
 import org.apache.camel.spi.TypeConvertible;
-import org.apache.camel.support.MessageHelper;
 import org.apache.camel.support.TypeConverterSupport;
 import org.apache.camel.support.service.ServiceSupport;
 import org.apache.camel.util.ObjectHelper;
@@ -660,15 +659,8 @@ public abstract class CoreTypeConverterRegistry extends 
ServiceSupport implement
                 return (TypeConversionException) cause;
             }
         }
-        Object body;
-        // extract the body for logging which allows to limit the message body 
in the exception/stacktrace
-        // and also can be used to turn off logging sensitive message data
-        if (exchange != null) {
-            body = MessageHelper.extractValueForLogging(value, 
exchange.getIn());
-        } else {
-            body = value;
-        }
-        return new TypeConversionException(body, type, cause);
+        // the message of the exception does not include the value, so use the 
value itself which gives the real from type
+        return new TypeConversionException(value, type, cause);
     }
 
     public Statistics getStatistics() {
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/impl/converter/CoreTypeConverterRegistryTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/impl/converter/CoreTypeConverterRegistryTest.java
index 8f7a57e19508..c13a0b566e36 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/impl/converter/CoreTypeConverterRegistryTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/impl/converter/CoreTypeConverterRegistryTest.java
@@ -17,6 +17,7 @@
 package org.apache.camel.impl.converter;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -25,17 +26,22 @@ import java.util.Set;
 
 import org.apache.camel.ContextTestSupport;
 import org.apache.camel.Exchange;
+import org.apache.camel.NoTypeConversionAvailableException;
+import org.apache.camel.TypeConversionException;
 import org.apache.camel.TypeConverter;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.spi.TypeConverterRegistry;
 import org.apache.camel.spi.TypeConvertible;
+import org.apache.camel.support.DefaultExchange;
 import org.apache.camel.support.TypeConverterSupport;
 import org.junit.jupiter.api.Test;
 
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class CoreTypeConverterRegistryTest extends ContextTestSupport {
@@ -311,4 +317,69 @@ public class CoreTypeConverterRegistryTest extends 
ContextTestSupport {
         assertInstanceOf(Set.class, tc.convertTo(Set.class, new String[] { 
"a", "b" }));
         assertNull(tc.convertTo(Set.class, new int[] { 1, 2 }));
     }
+
+    @Test
+    public void testTypeConversionExceptionHasFromTypeOfValue() {
+        context.getTypeConverterRegistry().addTypeConverter(Foo.class, 
Base.class, new TypeConverterSupport() {
+            @Override
+            public <T> T convertTo(Class<T> type, Exchange exchange, Object 
value) {
+                throw new IllegalStateException("cannot convert");
+            }
+        });
+        TypeConverter tc = context.getTypeConverter();
+        Exchange exchange = new DefaultExchange(context);
+        Sub value = new Sub();
+
+        TypeConversionException e = assertThrows(TypeConversionException.class,
+                () -> tc.convertTo(Foo.class, exchange, value));
+        assertSame(value, e.getValue());
+        assertEquals(Sub.class, e.getFromType());
+        assertEquals(Foo.class, e.getToType());
+        assertTrue(e.getMessage().startsWith("Error during type conversion 
from type: " + Sub.class.getCanonicalName()),
+                e.getMessage());
+
+        e = assertThrows(TypeConversionException.class, () -> 
tc.mandatoryConvertTo(Foo.class, exchange, value));
+        assertSame(value, e.getValue());
+        assertEquals(Sub.class, e.getFromType());
+    }
+
+    @Test
+    public void testCollectionWithElementThatDoesNotFitArray() throws 
Exception {
+        TypeConverter tc = context.getTypeConverter();
+        Exchange exchange = new DefaultExchange(context);
+
+        // a primitive array
+        assertNull(tc.convertTo(int[].class, exchange, List.of("a", "b")));
+        assertNull(tc.tryConvertTo(int[].class, exchange, List.of("a", "b")));
+        assertThrows(NoTypeConversionAvailableException.class,
+                () -> tc.mandatoryConvertTo(int[].class, exchange, 
List.of("a", "b")));
+        // a null element does not fit a primitive array
+        assertNull(tc.convertTo(int[].class, exchange, Arrays.asList(1, 
null)));
+        // an object array
+        assertNull(tc.convertTo(Integer[].class, exchange, List.of("a", "b")));
+        // CAMEL-26137: a list of strings to byte[] failed with argument type 
mismatch
+        assertNull(tc.convertTo(byte[].class, exchange, new 
ArrayList<>(List.of("a", "b"))));
+
+        // elements that fit still convert, also after the converter was 
promoted for the collection type
+        assertArrayEquals(new int[] { 1, 2 }, tc.convertTo(int[].class, 
exchange, List.of(1, 2)));
+        assertNull(tc.convertTo(int[].class, exchange, List.of("a", "b")));
+        assertArrayEquals(new Integer[] { 1, 2 }, 
tc.convertTo(Integer[].class, exchange, List.of(1, 2)));
+        assertNull(tc.convertTo(Integer[].class, exchange, List.of("a", "b")));
+    }
+
+    @Test
+    public void testArrayWithElementThatDoesNotFitArray() throws Exception {
+        TypeConverter tc = context.getTypeConverter();
+        Exchange exchange = new DefaultExchange(context);
+
+        assertNull(tc.convertTo(int[].class, exchange, new Object[] { "a", "b" 
}));
+        assertNull(tc.convertTo(int[].class, exchange, new Integer[] { 1, null 
}));
+        assertNull(tc.convertTo(Integer[].class, exchange, new Object[] { "a", 
"b" }));
+        assertThrows(NoTypeConversionAvailableException.class,
+                () -> tc.mandatoryConvertTo(int[].class, exchange, new 
Object[] { "a", "b" }));
+
+        // elements that fit still convert
+        assertArrayEquals(new int[] { 1, 2 }, tc.convertTo(int[].class, 
exchange, new Object[] { 1, 2 }));
+        assertArrayEquals(new Integer[] { 1, 2 }, 
tc.convertTo(Integer[].class, exchange, new int[] { 1, 2 }));
+    }
 }
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index 7c82ebd18930..a90240dafbb2 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -315,6 +315,18 @@ graphql, kubernetes-custom-resources and typesafe-ai) now 
have type converters t
 or `InputStream`, so such a body sent to http or a file failed. A `JsonObject` 
or `JsonArray` that holds a value that
 is not JSON, such as a POJO, converts as before.
 
+=== camel-core - TypeConversionException gives the value that failed to convert
+
+When a type converter fails during the conversion of a message, the 
`TypeConversionException` now gives the value that
+failed to convert from `getValue()`, and its type from `getFromType()` and in 
the message (`Error during type
+conversion from type: ...`). Before, they gave the value as a `String` 
prepared for logging, so the message always
+said `from type: java.lang.String`. The message does not include the value 
itself.
+
+The conversion of a collection or array to an array whose component type does 
not fit an element, such as a list of
+strings to `int[]` or `byte[]`, now gives no conversion (`null`, or 
`NoTypeConversionAvailableException` for a
+mandatory conversion), and other type converters can try. Before, it failed 
with a `TypeConversionException` due to
+`IllegalArgumentException: argument type mismatch` or an `ArrayStoreException`.
+
 === camel-core - a route called from a doCatch handles its failure after the 
doTry called a route that failed
 
 When the `doTry` block calls a route (such as with `direct`) that fails 
without handling the failure (no `onException`

Reply via email to