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

  1. Set both toolName and resultContains to non-blank values on the handoff.
  2. Ensure toolName matches a real tool registered on the agent.
  3. 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 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


AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14). Data as JSON: /api/errors/382f1fc3d06a6327. Report an issue: GitHub.