Skip to content

[BUG] Branch isolation uses a raw string prefix, leaking peer agents' output #1414

Description

@svetanis

Please make sure you read the contribution guide and file the issues in the right place.
Contribution guide.

🔴 Required Information

Describe the Bug

Contents.isEventBelongsToBranch decides which past events an agent may see, using a raw
String.startsWith:

// core/src/main/java/com/google/adk/flows/llmflows/Contents.java
return Strings.isNullOrEmpty(invocationBranch)
    || Strings.isNullOrEmpty(eventBranch)
    || invocationBranch.startsWith(eventBranch);

But a branch is a path of agent-name segments — BaseAgent builds it as branch + "." + name(), and
Event.branch()'s javadoc documents the format (agent_1.agent_2.agent_3) and the purpose ("Branch
is used when multiple sub-agent shouldn't see their peer agents' conversation history"
).

Matching inside a segment breaks that: "root.agent_10".startsWith("root.agent_1") is true, so
agent_10 receives peer agent_1's output. The trigger is a naming coincidence:
any two agents under one parent where one name prefixes the other (agent_1/agent_10,
search/search_v2).

Steps to Reproduce

Unit-level; no model call or credentials needed.

  1. Give an LlmAgent named agent_10 an InvocationContext with .branch("root.agent_10").
  2. Put one event in the session authored by agent_1 with .branch("root.agent_1").
  3. Run new Contents().processRequest(context, LlmRequest.builder().build()).
  4. Inspect result.updatedRequest().contents() — it contains the agent_1 event.

In an application the equivalent is a ParallelAgent with sub-agents agent_1 and agent_10:
agent_1 produces output, then agent_10's request is built.

Expected Behavior

agent_10's request contains no agent_1 events — they are peers, not ancestors.

Observed Behavior

The peer's output is included, re-authored as a user-role message:

[Content{parts=[Part{text=For context:},
                Part{text=[agent_1] said: sibling output}], role=user}]

No error, no warning, no log line.

Environment Details

  • ADK Library Version: 1.7.2-SNAPSHOT
  • OS: not OS-specific — pure string comparison

Model Information

N/A for the defect itself


🟡 Optional Information

Regression

No — the raw startsWith has been the comparison since the method was introduced.

Willingness to contribute

Yes — a PR follows immediately, requiring an exact match or a prefix ending on a segment boundary:

|| invocationBranch.equals(eventBranch)
|| invocationBranch.startsWith(eventBranch + ".");

How often has this issue occurred?

Always (100%), whenever two sibling agents' names collide on a prefix.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions