conductor-oss/conductor · error · IllegalArgumentException
SWARM handoff target must name a swarm agent: ${handoff.getT
Error message
SWARM handoff target must name a swarm agent: ${handoff.getTarget()} What it means
Thrown when a SWARM handoff's 'target' field is null or does not match any agent name in the swarm. The compiler builds a set of valid targets from the parent agent's name plus all sub-agent names (config.getName() + each agent in config.getAgents()), then checks each handoff's target against it.
Source
Thrown at agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/compiler/MultiAgentCompiler.java:1493
/** Validate the declarative SWARM contract without changing its wire representation. */
private static void validateSwarmHandoffs(AgentConfig config) {
if (config.getHandoffs() == null) return;
Set<String> targets = new HashSet<>();
targets.add(config.getName());
if (config.getAgents() != null) {
for (AgentConfig agent : config.getAgents()) targets.add(agent.getName());
}
for (HandoffConfig handoff : config.getHandoffs()) {
if (handoff == null
|| handoff.getType() == null
|| !Set.of("on_tool_result", "on_text_mention", "on_condition")
.contains(handoff.getType())) {
throw new IllegalArgumentException(
"SWARM handoff type must be on_tool_result, on_text_mention, or on_condition");
}
if (handoff.getTarget() == null || !targets.contains(handoff.getTarget())) {
throw new IllegalArgumentException(
"SWARM handoff target must name a swarm agent: " + handoff.getTarget());
}
switch (handoff.getType()) {
case "on_tool_result" -> {
if (isBlank(handoff.getToolName()) || isBlank(handoff.getResultContains())) {
throw new IllegalArgumentException(
"on_tool_result requires toolName and resultContains");
}
}
case "on_text_mention" -> {
if (isBlank(handoff.getText())) {
throw new IllegalArgumentException("on_text_mention requires text");
}
}
case "on_condition" -> {
if (isBlank(handoff.getTaskName())) {
throw new IllegalArgumentException(
"on_condition requires a nonblank taskName");View on GitHub (pinned to cf7c3e4a8a)
Solutions
- Ensure the handoff's target exactly matches the 'name' field of the parent agent or one of its sub-agents (case-sensitive).
- Check for typos — the target must be an exact string match against an agent name in the swarm.
- If you renamed an agent, update all handoff targets that point to the old name.
Example fix
// before: agent named "research-writer" but target says "research_writer"
{"type": "on_text_mention", "text": "handoff", "target": "research_writer"}
// after
{"type": "on_text_mention", "text": "handoff", "target": "research-writer"} Defensive patterns
Strategy: validation
Validate before calling
void validateHandoffTargets(AgentConfig config) {
Set<String> validTargets = new HashSet<>();
validTargets.add(config.getName());
if (config.getAgents() != null) {
for (AgentConfig a : config.getAgents()) validTargets.add(a.getName());
}
if (config.getHandoffs() != null) {
for (HandoffConfig h : config.getHandoffs()) {
if (h.getTarget() == null || !validTargets.contains(h.getTarget())) {
throw new IllegalArgumentException(
"Handoff target '" + h.getTarget() + "' not in valid targets: " + validTargets);
}
}
}
} Try / catch
try {
compiler.compile(agentConfig);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("handoff target must name a swarm agent")) {
String badTarget = extractTargetFromMessage(e.getMessage());
// cross-check against agent names and fix
}
throw e;
} Prevention
- Keep agent names and handoff targets in sync via a single source of truth.
- After renaming any agent, grep for its old name in handoff target fields.
When it happens
Trigger: A SWARM-strategy agent config where a HandoffConfig.target is null, has a typo, or references an agent name that doesn't exist in the swarm's agents list (including the parent agent's own name).
Common situations: Renaming a sub-agent but forgetting to update handoff targets, referencing a target by description instead of name, or a mismatch between the 'name' field and what 'target' points to. Also happens when the target uses different casing than the agent's declared name.
Related errors
- SWARM handoff type must be on_tool_result, on_text_mention,
- on_tool_result requires toolName and resultContains
- on_text_mention requires text
- on_condition requires a nonblank taskName
- Sub-agent name '${a.getName()}' in '${config.getName()}' is
AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14).
Data as JSON: /api/errors/f0f55a12d24551e8.
Report an issue: GitHub.