Federico Mariani created CAMEL-23945:
----------------------------------------
Summary: camel-langchain4j-agent: producer race on shared agent
field in agentFactory mode (fix needed on camel-4.18.x)
Key: CAMEL-23945
URL: https://issues.apache.org/jira/browse/CAMEL-23945
Project: Camel
Issue Type: Bug
Components: camel-langchain4j-agent
Affects Versions: 4.18.0, 4.15.0, 4.20.0
Reporter: Federico Mariani
Attachments: LangChain4jAgentFactoryConcurrencyTest.java
On 4.15.0 through 4.20.x (and the current {{camel-4.18.x}} branch),
{{LangChain4jAgentProducer.process()}} writes the agent resolved by the
{{AgentFactory}} to a *shared instance field* on every exchange and then
re-reads that field twice:
{code:java}
if (agentFactory != null) {
agent = agentFactory.createAgent(exchange); // field write per exchange
}
AiAgentBody<?> aiAgentBody = agent.processBody(messagePayload, exchange); //
field re-read
...
String response = agent.chat(aiAgentBody, toolProvider); //
field re-read again
{code}
A producer is a singleton per endpoint and {{process()}} must be thread-safe.
With two concurrent exchanges selecting different agents (e.g. a multi-agent
{{AgentFactory}} dispatching by header), one thread's write can land between
another thread's write and reads: the message is processed by the *wrong agent*
(wrong model, system prompt, guardrails and chat memory — the conversation turn
is persisted into the wrong agent's memory store). Because there are two
independent field reads, a single exchange can even {{processBody}} with one
agent and {{chat}} with another. The window is tiny so it does not reproduce in
demos, but under sustained concurrent load it is statistically inevitable.
*Status on main*: fixed incidentally by CAMEL-23697 (commit {{03d8361bbb74}},
4.21.0), which replaced the pattern with a local read: {{Agent agent =
agentFactory != null ? agentFactory.createAgent(exchange) : this.agent;}} — so
4.21.0+ is not affected.
*Requested fix*: backport the local-variable pattern (one line) to the
{{camel-4.18.x}} LTS branch. Introduced by CAMEL-22397 (commit
{{91ad52d80fb0}}, first released in 4.15.0).
Attached reproducer {{LangChain4jAgentFactoryConcurrencyTest}} compiles against
*camel-4.18.x* (not main, where the API changed with the fix): it makes the
race deterministic by blocking agent A inside {{processBody()}} — between the
producer's field write and its {{chat()}} field re-read — while a second
exchange selects agent B. The exchange that asked for agent A is answered by
agent B.
_This issue was drafted by Claude Code on behalf of Federico Mariani._
--
This message was sent by Atlassian Jira
(v8.20.10#820010)