{"record":{"id":"f0f55a12d24551e8","repo":"conductor-oss/conductor","slug":"swarm-handoff-target-must-name-a-swarm-agent-ha","errorCode":null,"errorMessage":"SWARM handoff target must name a swarm agent: ${handoff.getTarget()}","messagePattern":"SWARM handoff target must name a swarm agent: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/compiler/MultiAgentCompiler.java","lineNumber":1493,"sourceCode":"\n    /** Validate the declarative SWARM contract without changing its wire representation. */\n    private static void validateSwarmHandoffs(AgentConfig config) {\n        if (config.getHandoffs() == null) return;\n        Set<String> targets = new HashSet<>();\n        targets.add(config.getName());\n        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\");","sourceCodeStart":1475,"sourceCodeEnd":1511,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/compiler/MultiAgentCompiler.java#L1475-L1511","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before: agent named \"research-writer\" but target says \"research_writer\"\n{\"type\": \"on_text_mention\", \"text\": \"handoff\", \"target\": \"research_writer\"}\n// after\n{\"type\": \"on_text_mention\", \"text\": \"handoff\", \"target\": \"research-writer\"}","handlingStrategy":"validation","validationCode":"void validateHandoffTargets(AgentConfig config) {\n    Set<String> validTargets = new HashSet<>();\n    validTargets.add(config.getName());\n    if (config.getAgents() != null) {\n        for (AgentConfig a : config.getAgents()) validTargets.add(a.getName());\n    }\n    if (config.getHandoffs() != null) {\n        for (HandoffConfig h : config.getHandoffs()) {\n            if (h.getTarget() == null || !validTargets.contains(h.getTarget())) {\n                throw new IllegalArgumentException(\n                    \"Handoff target '\" + h.getTarget() + \"' not in valid targets: \" + validTargets);\n            }\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    compiler.compile(agentConfig);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"handoff target must name a swarm agent\")) {\n        String badTarget = extractTargetFromMessage(e.getMessage());\n        // cross-check against agent names and fix\n    }\n    throw e;\n}","preventionTips":["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."],"tags":["swarm","handoff","config-validation","agentspan","target-resolution"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}