{"record":{"id":"382f1fc3d06a6327","repo":"conductor-oss/conductor","slug":"on-tool-result-requires-toolname-and-resultcontain","errorCode":null,"errorMessage":"on_tool_result requires toolName and resultContains","messagePattern":"on_tool_result requires toolName and resultContains","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/compiler/MultiAgentCompiler.java","lineNumber":1499,"sourceCode":"        if (config.getAgents() != null) {\n            for (AgentConfig agent : config.getAgents()) targets.add(agent.getName());\n        }\n        for (HandoffConfig handoff : config.getHandoffs()) {\n            if (handoff == null\n                    || handoff.getType() == null\n                    || !Set.of(\"on_tool_result\", \"on_text_mention\", \"on_condition\")\n                            .contains(handoff.getType())) {\n                throw new IllegalArgumentException(\n                        \"SWARM handoff type must be on_tool_result, on_text_mention, or on_condition\");\n            }\n            if (handoff.getTarget() == null || !targets.contains(handoff.getTarget())) {\n                throw new IllegalArgumentException(\n                        \"SWARM handoff target must name a swarm agent: \" + handoff.getTarget());\n            }\n            switch (handoff.getType()) {\n                case \"on_tool_result\" -> {\n                    if (isBlank(handoff.getToolName()) || isBlank(handoff.getResultContains())) {\n                        throw new IllegalArgumentException(\n                                \"on_tool_result requires toolName and resultContains\");\n                    }\n                }\n                case \"on_text_mention\" -> {\n                    if (isBlank(handoff.getText())) {\n                        throw new IllegalArgumentException(\"on_text_mention requires text\");\n                    }\n                }\n                case \"on_condition\" -> {\n                    if (isBlank(handoff.getTaskName())) {\n                        throw new IllegalArgumentException(\n                                \"on_condition requires a nonblank taskName\");\n                    }\n                }\n                default -> throw new IllegalStateException(\"validated above\");\n            }\n        }\n    }","sourceCodeStart":1481,"sourceCodeEnd":1517,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/compiler/MultiAgentCompiler.java#L1481-L1517","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n{\"type\": \"on_tool_result\", \"target\": \"summarizer\", \"toolName\": \"fetch_data\"}\n// after\n{\"type\": \"on_tool_result\", \"target\": \"summarizer\", \"toolName\": \"fetch_data\", \"resultContains\": \"READY\"}","handlingStrategy":"validation","validationCode":"void validateOnToolResult(HandoffConfig h) {\n    if (\"on_tool_result\".equals(h.getType())) {\n        if (h.getToolName() == null || h.getToolName().isBlank()\n            || h.getResultContains() == null || h.getResultContains().isBlank()) {\n            throw new IllegalArgumentException(\"on_tool_result needs non-blank toolName and resultContains\");\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    compiler.compile(agentConfig);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"on_tool_result requires\")) {\n        // populate toolName and resultContains on the offending handoff\n    }\n    throw e;\n}","preventionTips":["When using on_tool_result, always specify both toolName and resultContains — neither is optional.","Use integration tests that exercise the handoff trigger path."],"tags":["swarm","handoff","on-tool-result","config-validation","agentspan"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}