[BUG] Semantic issue with repeated invocations of the same A2A agent
Describe the bug
The current A2A agent implementation reuses the same taskId and contextId when the same A2A agent is invoked multiple times.
This behavior is valid for some interrupted tasks, where multiple requests are expected to continue the same task. However, it causes a semantic problem when the A2A server completes the task during the first invocation.
For example, consider the following agent definition:
public interface EchoWithAgenticScopAgent {
@A2AClientAgent(
a2aServerUrl = A2A_ECHO_SERVER_URL,
outputKey = "response",
description = "Echo agent for testing contextId/taskId propagation")
ResultWithAgenticScope<String> echo(
@V("question") String question,
@A2AContextId @V("contextId") String contextId,
@A2ATaskId @V("taskId") String taskId);
}The same agent is then used twice in a sequence:
EchoWithAgenticScopAgent echoAgent = AgenticServices.a2aBuilder(
A2A_ECHO_SERVER_URL, EchoWithAgenticScopAgent.class)
.outputKey("response")
.build();
UntypedAgent seq = AgenticServices.sequenceBuilder()
.subAgents(echoAgent, echoAgent)
.build();
seq.invoke(Map.of("question", "message"));The server completes the task after the first invocation:
emitter.startWork();
emitter.addArtifact(List.of(new TextPart(response, null)));
emitter.complete();The second invocation then attempts to send another message using the same taskId. Since the task has already reached the terminal state TASK_STATE_COMPLETED, the A2A server rejects the request.
The resulting error is:
Cannot send message to task 33871e30-ad97-4189-a7bc-163abae8c5eb: task is in terminal state TASK_STATE_COMPLETED and cannot accept further messages: org.a2aproject.sdk.spec.UnsupportedOperationError
Log and Stack trace
[HttpClient-2-Worker-0] dev.langchain4j.agentic.a2a.DefaultA2AClientBuilder.handleStreamEnd()
ERROR: Streaming error occurred: Cannot send message to task
33871e30-ad97-4189-a7bc-163abae8c5eb: task is in terminal state
TASK_STATE_COMPLETED and cannot accept further messages:
org.a2aproject.sdk.spec.UnsupportedOperationError:
Cannot send message to task 33871e30-ad97-4189-a7bc-163abae8c5eb:
task is in terminal state TASK_STATE_COMPLETED and cannot accept further messagesTo Reproduce
Expected behavior
The main issue is the semantics of reusing taskId and contextId across repeated invocations.
Reusing the same identifiers is appropriate when the second request is intended to continue an interrupted task. However, it is not generally valid to reuse the same taskId after the previous invocation has already completed that task.
Therefore, the client needs to distinguish between:
Continuing an existing interrupted task, where reusing the existing taskId is expected; and Starting a new invocation after the previous task has reached a terminal state, where a new task should be created instead of sending a message to the completed task.
The current behavior makes these two cases indistinguishable when the same agent is invoked repeatedly.
This also creates a broader semantic concern regarding the documented Multi-turn conversations with A2A servers behavior. If the fix is implemented by changing the current identifier reuse semantics so that every invocation always creates a new task, this would potentially affect the existing contract around contextId and taskId propagation. In particular, it would change the meaning of identifiers stored in the agentic scope: identifiers that are currently reused across invocations could no longer always be reused for subsequent requests. Therefore, this does not appear to be only an implementation-level bug. Fixing it requires clarifying the intended semantics of taskId and contextId when an A2A agent is invoked multiple times, especially when the previous task has already reached a terminal state. The key question is whether repeated invocation of the same @A2AClientAgent should: continue the existing task when the task is in an interrupted state; create a new task when the existing task has reached a terminal state; and preserve the existing contextId where appropriate while using a new taskId. This distinction seems necessary to support both multi-turn/interrupted workflows and repeated independent invocations of the same A2A agent.
Source: langchain4j/langchain4j