{"record":{"id":"c2bbfaa315fc18cf","repo":"alibaba/spring-ai-alibaba","slug":"either-skill-name-or-skill-path-is-required","errorCode":null,"errorMessage":"Either skill_name or skill_path is required","messagePattern":"Either skill_name or skill_path is required","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":102,"sourceCode":"\t\tcatch (IllegalStateException e) {\n\t\t\tlogger.warn(\"Skill not found: {}\", e.getMessage());\n\t\t\treturn \"Error: \" + e.getMessage();\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tlogger.error(\"Error reading skill content: {}\", e.getMessage(), e);\n\t\t\treturn \"Error reading skill file: \" + e.getMessage();\n\t\t}\n\t\tcatch (Exception e) {\n\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}","sourceCodeStart":84,"sourceCodeEnd":120,"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#L84-L120","documentation":"readSkillContent requires at least one identifier for the skill: either skill_name or skill_path in the request. If both are absent (or blank after normalization) it throws this IllegalArgumentException instead of attempting an ambiguous lookup.","triggerScenarios":"An LLM invokes the read_skill tool with an empty or missing arguments object, or supplies unrelated keys so both skillName and skillPath normalize to null.","commonSituations":"Model hallucinating tool arguments or omitting them entirely; a caller constructing ReadSkillRequest with neither field set; JSON deserialization dropping fields with different names than expected.","solutions":["Include either \"skill_name\" (registered name) or \"skill_path\" (file path) in the tool-call arguments.","Validate the request object before calling apply/readSkillContent and return a corrective message to the model.","Improve the tool description so the model reliably passes one of the two identifiers."],"exampleFix":"// before\nString content = tool.apply(\"{}\"); // neither field set -> throws\n// after\nString content = tool.apply(\"{\\\"skill_name\\\": \\\"pdf-reader\\\"}\");","handlingStrategy":"validation","validationCode":"com.fasterxml.jackson.databind.JsonNode args = mapper.readTree(toolInput);\nif (!args.hasNonNull(\"skill_name\") && !args.hasNonNull(\"skill_path\")) {\n    return \"Error: provide either skill_name or skill_path\";\n}","typeGuard":"boolean hasIdentifier(ReadSkillRequest r) { return r != null && (r.skillName != null || r.skillPath != null); }","tryCatchPattern":"try { return tool.apply(input); } catch (IllegalArgumentException e) { return \"Bad arguments: \" + e.getMessage(); }","preventionTips":["Document both tool parameters clearly in the tool description.","Validate tool-call JSON before invoking apply.","Return corrective feedback to the LLM so it retries with a proper identifier."],"tags":["java","validation","tool-arguments"],"backgroundTag":"missing-required-argument","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"}