Serdar Gökay created CAMEL-24320:
------------------------------------

             Summary: camel-core - Kamelet route creation fails with virtual 
threads enabled on JDK 25
                 Key: CAMEL-24320
                 URL: https://issues.apache.org/jira/browse/CAMEL-24320
             Project: Camel
          Issue Type: Bug
          Components: camel-core, camel-kamelet
    Affects Versions: 4.21.0
         Environment: Apache Camel Main 4.21.0; Eclipse Temurin 25.0.3; 
camel.threads.virtual.enabled=true; reproduced in Docker. Control: the 
identical route starts and consumes timer ticks with virtual threads disabled.
            Reporter: Serdar Gökay
         Attachments: camel-kamelet-vt-jdk25-reproducer.zip

h3. What happens

With {{-Dcamel.threads.virtual.enabled=true}} on JDK 25, a route that
consumes a Kamelet fails during route creation with a
{{NullPointerException}}. The same route starts and runs normally with
virtual threads disabled.

h3. Root cause

# On JDK 25 with virtual threads enabled, the multi-release
{{ContextValueFactory}} ({{core/camel-util}}, Java 25 variant) selects the
{{ScopedValue}}-backed {{ScopedValueContextValue}} because
{{ThreadType.current()}} reports {{VIRTUAL}}.
# {{DefaultCamelContextExtension.getCreateRoute()}} and
{{getCreateProcessor()}} are implemented as {{isCreateRoute.orElse(null)}}
and {{isCreateProcessor.orElse(null)}}
({{DefaultCamelContextExtension.java:330,335}}).
# {{ScopedValueContextValue.orElse}} ({{ContextValueFactory.java:141}})
delegates to {{java.lang.ScopedValue.orElse}}, which on JDK 25 executes
{{Objects.requireNonNull(other)}} before any binding lookup — a null
fallback therefore throws unconditionally, even where a binding exists.
# {{KameletComponent}} reads {{getCreateRoute()}} while initializing its
endpoint service ({{KameletComponent.java:184}}), outside any binding scope,
so every Kamelet route creation throws NPE before the route exists.

There is no configuration that keeps virtual threads enabled while forcing
the {{ThreadLocal}}-backed {{ContextValue}}; the implementation is selected
solely from {{ThreadType.current()}}. The defect is still present on {{main}}
as of 2026-07-31 (call sites and implementation unchanged).

h3. Stack trace (camel 4.21.0, Temurin 25.0.3)

{code}
Exception in thread "main" org.apache.camel.FailedToCreateRouteException: 
Failed to create route: vt-kamelet-repro (source: file:route.camel.yaml): 
Route(vt-kamelet-repro)[From[kamelet:vt-repro-source] -> [Lo... because: Failed 
to resolve endpoint: kamelet://vt-repro-source due to: null
        at 
org.apache.camel.reifier.RouteReifier.createRoute(RouteReifier.java:96)
        ...
Caused by: org.apache.camel.ResolveEndpointFailedException: Failed to resolve 
endpoint: kamelet://vt-repro-source due to: null
        at 
org.apache.camel.impl.engine.AbstractCamelContext.doGetEndpoint(AbstractCamelContext.java:891)
        ...
Caused by: java.lang.NullPointerException
        at java.base/java.util.Objects.requireNonNull(Unknown Source)
        at java.base/java.lang.ScopedValue.orElse(Unknown Source)
        at 
org.apache.camel.util.concurrent.ContextValueFactory$ScopedValueContextValue.orElse(ContextValueFactory.java:141)
        at 
org.apache.camel.impl.engine.DefaultCamelContextExtension.getCreateRoute(DefaultCamelContextExtension.java:330)
        at 
org.apache.camel.component.kamelet.KameletComponent$1.doInit(KameletComponent.java:184)
{code}

h3. Reproducer (plain Camel Main, two small files)

{{route.camel.yaml}}:
{code}
- route:
    id: vt-kamelet-repro
    from:
      uri: kamelet:vt-repro-source
      steps:
        - log: "consumed ${body}"
{code}

{{kamelets/vt-repro-source.kamelet.yaml}}:
{code}
apiVersion: camel.apache.org/v1
kind: Kamelet
metadata:
  name: vt-repro-source
  labels:
    camel.apache.org/kamelet.type: source
spec:
  definition:
    title: VT repro source
  template:
    from:
      uri: timer:tick
      parameters:
        period: "1000"
      steps:
        - setBody:
            constant: hello
        - to:
            uri: kamelet:sink
{code}

Run with any JDK 25 and camel-main 4.21.0 (kamelets/ on the classpath):

{code}
java -cp <camel-main-4.21.0-classpath>:. \
  -Dcamel.threads.virtual.enabled=true \
  -Dcamel.main.routesIncludePattern=file:route.camel.yaml \
  org.apache.camel.main.Main
{code}

→ {{FailedToCreateRouteException}} with the NPE above at startup.
With {{-Dcamel.threads.virtual.enabled=false}} the identical files start and
consume timer ticks.

h3. Suggested fix direction

Make the {{ScopedValue}}-backed {{orElse}} null-tolerant — e.g. return the
default when unbound instead of delegating a null fallback
({{scopedValue.isBound() ? scopedValue.get() : defaultValue}}) — or change
the two call sites to a non-null fallback. A regression test that creates a
Kamelet route with virtual threads enabled on JDK 25 would cover the path;
note that {{getCreateProcessor()}} carries the same defect for any component
reading it during endpoint creation.

Related: CAMEL-20199 (virtual threads umbrella).




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to