{"record":{"id":"77fde9f22547f52d","repo":"alibaba/spring-ai-alibaba","slug":"skill-path-cannot-be-null-or-empty","errorCode":null,"errorMessage":"Skill path cannot be null or empty","messagePattern":"Skill path cannot be null or empty","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/skills/registry/AbstractSkillRegistry.java","lineNumber":123,"sourceCode":"\t}\n\n\t@Override\n\tpublic boolean disableByPath(String skillPath) {\n\t\treturn findByPathInternal(skillPath)\n\t\t\t\t.map(SkillMetadata::getName)\n\t\t\t\t.map(this::disable)\n\t\t\t\t.orElse(false);\n\t}\n\n\t@Override\n\tpublic boolean isDisabled(String name) {\n\t\treturn name != null && disabledSkillNames.contains(name);\n\t}\n\n\t@Override\n\tpublic String readSkillContentByPath(String skillPath) throws IOException {\n\t\tif (skillPath == null || skillPath.isBlank()) {\n\t\t\tthrow new IllegalArgumentException(\"Skill path cannot be null or empty\");\n\t\t}\n\t\trequireNormalizedSkillPath(skillPath);\n\t\tSkillMetadata skill = getByPath(skillPath)\n\t\t\t\t.orElseThrow(() -> new IllegalStateException(\"Skill not found: \" + skillPath));\n\t\treturn skill.loadFullContent();\n\t}\n\n\tprotected abstract void loadSkillsToRegistry();\n\n\tprotected static String normalizeSkillPath(String skillPath) {\n\t\ttry {\n\t\t\treturn Path.of(skillPath).toAbsolutePath().normalize().toString();\n\t\t}\n\t\tcatch (InvalidPathException ex) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid skill path: \" + skillPath, ex);\n\t\t}\n\t}\n","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/skills/registry/AbstractSkillRegistry.java#L105-L141","documentation":"AbstractSkillRegistry.readSkillContentByPath validates that the given skill path is non-null and non-blank before normalizing and looking it up, throwing IllegalArgumentException on blank input. A path is mandatory to locate the registered skill whose full content should be read.","triggerScenarios":"Calling readSkillContentByPath(null) or readSkillContentByPath(\"\") / whitespace-only string, typically when the path originated from an unresolved lookup or empty tool argument.","commonSituations":"An LLM tool call supplies an empty skill_path argument; upstream code propagates a null path after a failed skill lookup; template variables left unsubstituted.","solutions":["Ensure callers pass a real skill path obtained from the registry (e.g. from SkillMetadata.getSkillPath())","Guard the input: check skillPath != null && !skillPath.isBlank() before calling","Verify the skill was registered so a valid path exists in the first place"],"exampleFix":"// before\nString content = registry.readSkillContentByPath(skillPath);\n// after\nif (skillPath == null || skillPath.isBlank()) { throw new IllegalArgumentException(\"skillPath must be provided\"); }\nString content = registry.readSkillContentByPath(skillPath);","handlingStrategy":"type-guard","validationCode":"if (skillPath == null || skillPath.isBlank()) { throw new IllegalArgumentException(\"skillPath required\"); }","typeGuard":"boolean validSkillPath(String p) { return p != null && !p.isBlank(); }","tryCatchPattern":"try { content = registry.readSkillContentByPath(p); } catch (IllegalArgumentException e) { log.warn(\"Bad skill path: {}\", e.getMessage()); }","preventionTips":["Source paths from SkillMetadata.getSkillPath(), never from unvalidated input","Reject empty tool-call arguments before invoking registry APIs","Log the origin of null/blank paths to catch upstream lookup bugs"],"tags":["skill","null-check","validation"],"backgroundTag":"null-argument","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}