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

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions