{"record":{"id":"238ae56f51d43b04","repo":"conductor-oss/conductor","slug":"skill-package-contains-duplicate-script-tool-name","errorCode":null,"errorMessage":"Skill package contains duplicate script tool name: {toolName}","messagePattern":"Skill package contains duplicate script tool name: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/service/SkillRegistryService.java","lineNumber":514,"sourceCode":"        Map<String, String> agentFiles = new LinkedHashMap<>();\n        Map<String, Map<String, Object>> scripts = new LinkedHashMap<>();\n        List<String> resourceFiles = new ArrayList<>();\n\n        for (String path : contentByPath.keySet()) {\n            if (path.equals(\"SKILL.md\")) {\n                continue;\n            }\n            if (isRootAgentFile(path)) {\n                agentFiles.put(\n                        path.substring(0, path.length() - \"-agent.md\".length()),\n                        decodeUtf8(path, contentByPath.get(path)));\n                continue;\n            }\n            if (isScriptFile(path)) {\n                String filename = path.substring(\"scripts/\".length());\n                String toolName = scriptToolName(filename);\n                if (scripts.containsKey(toolName)) {\n                    throw new IllegalArgumentException(\n                            \"Skill package contains duplicate script tool name: \" + toolName);\n                }\n                Map<String, Object> info = new LinkedHashMap<>();\n                info.put(\"filename\", filename);\n                info.put(\"language\", detectScriptLanguage(filename));\n                scripts.put(toolName, info);\n                continue;\n            }\n            if (isResourceFile(path)) {\n                resourceFiles.add(path);\n            }\n        }\n\n        Map<String, Object> rawConfig = new LinkedHashMap<>();\n        rawConfig.put(\"model\", resolveModel(manifest));\n        rawConfig.put(\"agentModels\", resolveAgentModels(manifest));\n        rawConfig.put(\"skillMd\", skillMd);\n        rawConfig.put(\"agentFiles\", agentFiles);","sourceCodeStart":496,"sourceCodeEnd":532,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/service/SkillRegistryService.java#L496-L532","documentation":"Thrown by parseSkillPackage when two script files under scripts/ map to the same tool name. scriptToolName strips the file extension, so scripts/run.py and scripts/run.sh both become the tool name 'run'. Skill scripts are exposed as tools by their base filename, so tool names must be unique within the package.","triggerScenarios":"POST /api/skills/register with a zip containing scripts/run.py AND scripts/run.sh (or scripts/parse.js and scripts/parse.ts). The second one encountered triggers the duplicate detection.","commonSituations":"Providing both a Python and a shell variant of the same tool; porting a script to a new language and leaving the old one in scripts/; two scripts that differ only by extension.","solutions":["Rename one of the scripts so the base filenames differ, e.g. scripts/run.py and scripts/run-shell.sh.","Remove the obsolete script variant before zipping.","Keep exactly one language per logical tool name."],"exampleFix":"# before: scripts/run.py and scripts/run.sh collide on tool name 'run'\n# after: rename one\nmv scripts/run.sh scripts/run-bash.sh","handlingStrategy":"validation","validationCode":"// Before zipping, ensure script base filenames are unique\nSet<String> toolNames = new HashSet<>();\nfor (Path p : scriptsDirFiles) {\n    String fn = p.getFileName().toString();\n    String tool = fn.contains(\".\") ? fn.substring(0, fn.lastIndexOf('.')) : fn;\n    if (!toolNames.add(tool)) throw new IllegalStateException(\"duplicate tool name: \" + tool);\n}","typeGuard":"static boolean uniqueScriptToolNames(List<String> scriptFilenames) {\n    Set<String> seen = new HashSet<>();\n    for (String fn : scriptFilenames) {\n        String tool = fn.contains(\".\") ? fn.substring(0, fn.lastIndexOf('.')) : fn;\n        if (!seen.add(tool)) return false;\n    }\n    return true;\n}","tryCatchPattern":"try { skillRegistryService.register(manifest, pkg); }\ncatch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"duplicate script tool name\")) { /* rename one script */ }\n    else throw e;\n}","preventionTips":["Keep one language per logical tool; remove superseded variants before zipping.","Name scripts by their distinct purpose, not just by extension."],"tags":["java","conductor","skill-registry","scripts","validation","package-integrity"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}