{"record":{"id":"25efe5b9708798dc","repo":"alibaba/spring-ai-alibaba","slug":"skill-name-and-skill-path-must-refer-to-the-same-s","errorCode":null,"errorMessage":"skill_name and skill_path must refer to the same skill","messagePattern":"skill_name and skill_path must refer to the same skill","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/hook/skills/ReadSkillTool.java","lineNumber":111,"sourceCode":"\t\t\tlogger.error(\"Unexpected error reading skill: {}\", e.getMessage(), e);\n\t\t\treturn \"Error: \" + e.getMessage();\n\t\t}\n\t}\n\n\tprivate String readSkillContent(ReadSkillRequest request) throws IOException {\n\t\tString skillName = normalize(request != null ? request.skillName : null);\n\t\tString skillPath = normalize(request != null ? request.skillPath : null);\n\t\tif (skillName == null && skillPath == null) {\n\t\t\tthrow new IllegalArgumentException(\"Either skill_name or skill_path is required\");\n\t\t}\n\n\t\tif (skillName != null && skillPath != null) {\n\t\t\tSkillMetadata skillByName = skillRegistry.get(skillName)\n\t\t\t\t\t.orElseThrow(() -> new IllegalStateException(\"Skill not found: \" + skillName));\n\t\t\tSkillMetadata skillByPath = skillRegistry.getByPath(skillPath)\n\t\t\t\t\t.orElseThrow(() -> new IllegalStateException(\"Skill not found: \" + skillPath));\n\t\t\tif (!skillByName.getName().equals(skillByPath.getName())) {\n\t\t\t\tthrow new IllegalArgumentException(\"skill_name and skill_path must refer to the same skill\");\n\t\t\t}\n\t\t\treturn skillRegistry.readSkillContent(skillByName.getName());\n\t\t}\n\n\t\tif (skillName != null) {\n\t\t\treturn skillRegistry.readSkillContent(skillName);\n\t\t}\n\t\treturn skillRegistry.readSkillContentByPath(skillPath);\n\t}\n\n\tprivate static String normalize(String value) {\n\t\treturn StringUtils.hasText(value) ? value.trim() : null;\n\t}\n\n\t/**\n\t * Request structure for reading a skill.\n\t */\n\tpublic static class ReadSkillRequest {","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/hook/skills/ReadSkillTool.java#L93-L129","documentation":"When both skill_name and skill_path are provided, the tool resolves both via the registry and verifies they denote the same skill (compared by name). If the name and path point to different skills it throws this IllegalArgumentException to prevent reading an ambiguous/unintended skill.","triggerScenarios":"Invoking the read_skill tool with skill_name=\"a\" and skill_path pointing to skill \"b\"'s directory; stale registry state where the path mapping changed after the model cached a pair of arguments.","commonSituations":"LLM combining a remembered skill name with a guessed path; skills renamed or moved on disk so path and name diverge; copy-paste mistakes in hand-built tool calls.","solutions":["Pass only one of skill_name or skill_path, not both, unless you are sure they match.","Rebuild/refresh the SkillRegistry so path mappings reflect current skill names.","Verify the registered name equals the skill at the given path before sending both fields."],"exampleFix":"// before\nString content = tool.apply(\"{\\\"skill_name\\\": \\\"summarizer\\\", \\\"skill_path\\\": \\\"/skills/translator\\\"}\");\n// after\nString content = tool.apply(\"{\\\"skill_name\\\": \\\"summarizer\\\"}\");","handlingStrategy":"validation","validationCode":"if (req.skillName != null && req.skillPath != null) {\n    var byName = registry.get(req.skillName);\n    var byPath = registry.getByPath(req.skillPath);\n    if (byName.isPresent() && byPath.isPresent() && !byName.get().getName().equals(byPath.get().getName())) {\n        throw new IllegalArgumentException(\"skill_name/skill_path mismatch\");\n    }\n}","typeGuard":"boolean consistent(SkillRegistry reg, String name, String path) {\n    return name == null || path == null ||\n        reg.get(name).equals(reg.getByPath(path));\n}","tryCatchPattern":"try { return tool.apply(input); } catch (IllegalArgumentException e) { return \"Ambiguous skill reference: \" + e.getMessage(); }","preventionTips":["Pass only skill_name or only skill_path unless both are verified.","Refresh the registry after renaming/moving skills on disk.","Sanitize model-supplied arguments instead of trusting combined name+path."],"tags":["java","validation","tool-arguments"],"backgroundTag":"invalid-argument-value","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}