{"record":{"id":"dcafaf53657d5824","repo":"conductor-oss/conductor","slug":"plan-source-tool-toolname-is-not-registered-a","errorCode":null,"errorMessage":"plan_source.tool '${toolName}' is not registered as a harness-level tool on '${config.getName()}'. The plan_reader task is emitted in the harness's task namespace, so the tool must be declared in tools=[...] on the harness itself (declaring it on a sub-agent does not work).","messagePattern":"plan_source\\.tool '(.+?)' is not registered as a harness-level tool on '(.+?)'\\. The plan_reader task is emitted in the harness's task namespace, so the tool must be declared in tools=\\[\\.\\.\\.\\] on the harness itself \\(declaring it on a sub-agent does not work\\)\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/compiler/MultiAgentCompiler.java","lineNumber":2755,"sourceCode":"        // to retrieve the plan from an external source. This provides a deterministic\n        // fallback: even if the planner's text output fails extraction, the plan can\n        // be read directly from where the explorer wrote it.\n        //\n        // Validate at compile time that ``planSource.tool`` is a real tool registered\n        // somewhere in the harness — a typo is silently swallowed if we wait until\n        // runtime (the ``optional:true`` task simply doesn't run, extraction falls\n        // through to the no_plan branch). Reject the harness here so the misconfig\n        // surfaces at deploy.\n        String planReaderRef = null;\n        if (config.getPlanSource() != null) {\n            Map<String, Object> planSource = config.getPlanSource();\n            String toolName = (String) planSource.get(\"tool\");\n            if (toolName == null || toolName.isBlank()) {\n                throw new IllegalArgumentException(\n                        \"plan_source must include a non-empty 'tool' field\");\n            }\n            if (!isToolRegisteredInHarness(config, toolName)) {\n                throw new IllegalArgumentException(\n                        \"plan_source.tool '\"\n                                + toolName\n                                + \"' is not registered as a harness-level tool on '\"\n                                + config.getName()\n                                + \"'. The plan_reader task is emitted in the harness's task \"\n                                + \"namespace, so the tool must be declared in tools=[...] on the harness itself \"\n                                + \"(declaring it on a sub-agent does not work).\");\n            }\n            @SuppressWarnings(\"unchecked\")\n            Map<String, Object> toolArgs =\n                    (Map<String, Object>) planSource.getOrDefault(\"args\", Map.of());\n\n            planReaderRef = prefix + \"_plan_reader\";\n            WorkflowTask planReaderTask = new WorkflowTask();\n            planReaderTask.setName(toolName);\n            planReaderTask.setTaskReferenceName(planReaderRef);\n            planReaderTask.setType(\"SIMPLE\");\n","sourceCodeStart":2737,"sourceCodeEnd":2773,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/compiler/MultiAgentCompiler.java#L2737-L2773","documentation":"Thrown when a PLAN_EXECUTE agent's planSource.tool names a tool that is not registered at the harness (parent agent) level. The plan_reader task is emitted in the harness's task namespace, so the tool must be declared in the harness's own tools=[...] list — declaring it on a sub-agent does not work because the task runs in the parent's scope.","triggerScenarios":"An AgentConfig with strategy=PLAN_EXECUTE where planSource.tool is a non-blank string that does not match any tool name in config.getTools() (the harness-level tools). The tool may exist on a sub-agent but that doesn't count.","commonSituations":"Declaring the plan-reading tool on a sub-agent (e.g., the planner) instead of on the harness itself, or using a tool name that exists in a different agent definition. The plan_reader task runs in the harness namespace, so only harness-level tools are visible.","solutions":["Move the tool declaration to the harness-level tools=[...] list on the parent AgentConfig.","Verify the tool name in planSource.tool exactly matches the name field of a tool in config.getTools().","The tool does NOT need to be on the planner or any sub-agent — it must be on the harness."],"exampleFix":"// before: tool declared on sub-agent, not harness\nAgentConfig.builder()\n    .strategy(Strategy.PLAN_EXECUTE)\n    .planner(plannerAgent)\n    .planSource(Map.of(\"tool\", \"read_skill_file\"))\n    // read_skill_file is only on plannerAgent.tools\n    .tools(List.of())  // harness has no tools!\n    .build();\n// after: tool declared on harness\nAgentConfig.builder()\n    .strategy(Strategy.PLAN_EXECUTE)\n    .planner(plannerAgent)\n    .planSource(Map.of(\"tool\", \"read_skill_file\"))\n    .tools(List.of(ToolConfig.builder().name(\"read_skill_file\").build()))\n    .build();","handlingStrategy":"validation","validationCode":"void validatePlanSourceTool(AgentConfig config) {\n    Map<String, Object> ps = config.getPlanSource();\n    if (ps == null) return;\n    String toolName = (String) ps.get(\"tool\");\n    if (toolName == null || toolName.isBlank()) return;  // handled by [30]\n    Set<String> harnessTools = new HashSet<>();\n    if (config.getTools() != null) {\n        for (ToolConfig t : config.getTools()) harnessTools.add(t.getName());\n    }\n    if (!harnessTools.contains(toolName)) {\n        throw new IllegalArgumentException(\n            \"plan_source.tool '\" + toolName + \"' not in harness tools: \" + harnessTools);\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    compiler.compile(agentConfig);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"not registered as a harness-level tool\")) {\n        // move the tool declaration from sub-agent to harness tools=[...]\n    }\n    throw e;\n}","preventionTips":["plan_source.tool must be declared in the harness's own tools=[...] — not on any sub-agent.","The plan_reader task runs in the harness namespace; sub-agent tools are not visible."],"tags":["plan-execute","plan-source","tool-registration","harness","config-validation","agentspan"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}