conductor-oss/conductor · error · IllegalArgumentException
on_tool_result requires toolName and resultContains
Error message
on_tool_result requires toolName and resultContains
What it means
Thrown when a SWARM handoff of type 'on_tool_result' is missing either the 'toolName' or 'resultContains' field (or both are blank/null). These two fields define the trigger condition: when the named tool returns a result containing the specified substring, the handoff fires. The compiler checks them after validating the type.
Source
Thrown at agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/compiler/MultiAgentCompiler.java:1499
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");
}
}
default -> throw new IllegalStateException("validated above");
}
}
}View on GitHub (pinned to cf7c3e4a8a)
Solutions
- Set both toolName and resultContains to non-blank values on the handoff.
- Ensure toolName matches a real tool registered on the agent.
- Ensure resultContains is a meaningful substring that the tool's output would actually contain.
Example fix
// before
{"type": "on_tool_result", "target": "summarizer", "toolName": "fetch_data"}
// after
{"type": "on_tool_result", "target": "summarizer", "toolName": "fetch_data", "resultContains": "READY"} Defensive patterns
Strategy: validation
Validate before calling
void validateOnToolResult(HandoffConfig h) {
if ("on_tool_result".equals(h.getType())) {
if (h.getToolName() == null || h.getToolName().isBlank()
|| h.getResultContains() == null || h.getResultContains().isBlank()) {
throw new IllegalArgumentException("on_tool_result needs non-blank toolName and resultContains");
}
}
} Try / catch
try {
compiler.compile(agentConfig);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("on_tool_result requires")) {
// populate toolName and resultContains on the offending handoff
}
throw e;
} Prevention
- When using on_tool_result, always specify both toolName and resultContains — neither is optional.
- Use integration tests that exercise the handoff trigger path.
When it happens
Trigger: A HandoffConfig with type='on_tool_result' where toolName is null/blank or resultContains is null/blank. The check uses isBlank() which rejects both null and whitespace-only strings.
Common situations: Forgetting to populate both required fields when configuring an on_tool_result handoff, or leaving one as an empty string assuming it has a default. Common when handoffs are built from templates that don't fill in both slots.
Related errors
- SWARM handoff type must be on_tool_result, on_text_mention,
- SWARM handoff target must name a swarm agent: ${handoff.getT
- 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/382f1fc3d06a6327.
Report an issue: GitHub.