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

luigidemasi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 9687273ecdeddc2c7d7f5c77c12747a0e5cb4eaa
Author: Luigi De Masi <[email protected]>
AuthorDate: Thu Sep 24 09:21:08 2026 +0200

    CAMEL-24977: Add context to invalid semantic numeric values
    
    Report the question, field and YAML source location when threshold or
    uncertainty cannot be parsed as a number. Preserve the original parsing 
cause.
    
    Clarify that raw property configurers receive expanded placeholders only 
when
    the caller requests placeholder resolution.
    
    Test both fields through the route loader without schema validation, 
including
    the resource name and exact source position in the diagnostic.
    
    Co-authored-by: Codex <[email protected]>
    Signed-off-by: Luigi De Masi <[email protected]>
---
 .../org/apache/camel/spi/PropertyConfigurer.java   |  7 +++---
 .../SemanticDefinitionDeserializer.java            | 18 ++++++++++++---
 .../camel/dsl/yaml/SemanticQuestionTest.java       | 27 ++++++++++++++++++++++
 3 files changed, 46 insertions(+), 6 deletions(-)

diff --git 
a/core/camel-api/src/main/java/org/apache/camel/spi/PropertyConfigurer.java 
b/core/camel-api/src/main/java/org/apache/camel/spi/PropertyConfigurer.java
index a0929a3238f1..de1e867889ec 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/PropertyConfigurer.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/PropertyConfigurer.java
@@ -38,9 +38,10 @@ import org.apache.camel.CamelContext;
 public interface PropertyConfigurer {
 
     /**
-     * Optionally binds a value before resolving bean or class references. 
Property placeholders have already been
-     * resolved. This allows an option that owns reference resolution and 
lifecycle to retain the reference text.
-     * Returning false leaves the normal binding behavior unchanged.
+     * Optionally binds a value before resolving bean or class references. 
Property placeholders are resolved before
+     * this call when placeholder resolution was requested by the caller. This 
allows an option that owns reference
+     * resolution and lifecycle to retain the reference text. Returning false 
leaves the normal binding behavior
+     * unchanged.
      *
      * @param  camelContext the Camel context
      * @param  target       the target instance
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/SemanticDefinitionDeserializer.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/SemanticDefinitionDeserializer.java
index 64d93f41b03b..dd5bb10375a7 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/SemanticDefinitionDeserializer.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/SemanticDefinitionDeserializer.java
@@ -25,6 +25,7 @@ import java.util.Set;
 import org.apache.camel.CamelContext;
 import org.apache.camel.dsl.yaml.common.YamlDeserializationContext;
 import org.apache.camel.dsl.yaml.common.YamlDeserializerSupport;
+import org.apache.camel.dsl.yaml.common.exception.YamlDeserializationException;
 import org.apache.camel.semantic.SemanticQuestion;
 import org.apache.camel.semantic.SemanticQuestions;
 import org.apache.camel.spi.CamelContextCustomizer;
@@ -110,13 +111,24 @@ public class SemanticDefinitionDeserializer extends 
YamlDeserializerSupport impl
                     : SemanticQuestion.UncertaintyPolicy.FAIL;
             result.put(name, new SemanticQuestion(
                     type, asText(values.get("instructions")), 
asText(values.get("state")),
-                    criteria, levels, number(values, "threshold", 0.5), 
number(values, "uncertainty", 0), policy));
+                    criteria, levels, number(values, name, "threshold", 0.5), 
number(values, name, "uncertainty", 0), policy));
         });
         return result;
     }
 
-    private static double number(Map<String, Node> values, String name, double 
fallback) {
-        return values.containsKey(name) ? 
Double.parseDouble(asText(values.get(name))) : fallback;
+    private static double number(Map<String, Node> values, String question, 
String name, double fallback) {
+        if (!values.containsKey(name)) {
+            return fallback;
+        }
+        Node node = values.get(name);
+        String raw = asText(node);
+        try {
+            return Double.parseDouble(raw);
+        } catch (NumberFormatException e) {
+            throw new YamlDeserializationException(
+                    node,
+                    "Invalid numeric value for '" + name + "' in semantic 
question '" + question + "': " + raw, e);
+        }
     }
 
     private static Map<String, Node> fields(Node node) {
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/SemanticQuestionTest.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/SemanticQuestionTest.java
index 4b71a86826ae..5633c8726685 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/SemanticQuestionTest.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/SemanticQuestionTest.java
@@ -22,6 +22,7 @@ import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.dsl.yaml.common.exception.YamlDeserializationException;
 import org.apache.camel.dsl.yaml.support.YamlTestSupport;
 import org.apache.camel.language.semantic.SemanticLanguage;
 import org.apache.camel.semantic.SemanticAdapter;
@@ -34,6 +35,8 @@ import org.apache.camel.support.ResourceHelper;
 import org.apache.camel.support.RouteWatcherReloadStrategy;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -206,4 +209,28 @@ class SemanticQuestionTest extends YamlTestSupport {
         assertThatThrownBy(() -> 
loadRoutesNoValidate(declarations("${body}").replace("type: choice", "type: 
score")))
                 .hasStackTraceContaining("Node type map is invalid, expected 
array");
     }
+
+    @ParameterizedTest
+    @ValueSource(strings = { "threshold", "uncertainty" })
+    void invalidNumericValuesIdentifyQuestionFieldAndLocation(String field) {
+        String yaml = """
+                - semantic:
+                    question:
+                      spam:
+                        type: boolean
+                        instructions: Is this spam?
+                        %s: abc
+                """.formatted(field);
+        assertThatThrownBy(() -> loadRoutesNoValidate(yaml))
+                .hasMessageContaining("route-0.yaml")
+                .hasRootCauseInstanceOf(NumberFormatException.class)
+                
.cause().isInstanceOfSatisfying(YamlDeserializationException.class, error -> {
+                    assertThat(error).hasMessageContaining(
+                            "Invalid numeric value for '" + field + "' in 
semantic question 'spam': abc");
+                    assertThat(error.getProblemMark()).hasValueSatisfying(mark 
-> {
+                        assertThat(mark.getLine()).isEqualTo(5);
+                        assertThat(mark.getColumn()).isEqualTo(8 + 
field.length() + 2);
+                    });
+                });
+    }
 }

Reply via email to