{"record":{"id":"66dda6e6a859fa6b","repo":"alibaba/spring-ai-alibaba","slug":"skill-path-is-required","errorCode":null,"errorMessage":"Skill path is required","messagePattern":"Skill path is required","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/skills/SkillMetadata.java","lineNumber":172,"sourceCode":"\t\tpublic Builder fullContent(String fullContent) {\n\t\t\tmetadata.fullContent = fullContent;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder allowedTools(List<String> allowedTools) {\n\t\t\tmetadata.allowedTools = allowedTools == null ? List.of() : List.copyOf(allowedTools);\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic SkillMetadata build() {\n\t\t\tif (metadata.name == null || metadata.name.isEmpty()) {\n\t\t\t\tthrow new IllegalStateException(\"Skill name is required\");\n\t\t\t}\n\t\t\tif (metadata.description == null || metadata.description.isEmpty()) {\n\t\t\t\tthrow new IllegalStateException(\"Skill description is required\");\n\t\t\t}\n\t\t\tif (metadata.skillPath == null || metadata.skillPath.isEmpty()) {\n\t\t\t\tthrow new IllegalStateException(\"Skill path is required\");\n\t\t\t}\n\t\t\tif (metadata.allowedTools == null) {\n\t\t\t\tmetadata.allowedTools = List.of();\n\t\t\t}\n\t\t\treturn metadata;\n\t\t}\n\t}\n}\n","sourceCodeStart":154,"sourceCodeEnd":181,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/skills/SkillMetadata.java#L154-L181","documentation":"SkillMetadata.Builder.build() validates that skillPath (the file system location of the skill) is set and non-empty, throwing IllegalStateException otherwise. Without a path the library cannot locate or load the skill's files. This is a fail-fast invariant check during metadata construction.","triggerScenarios":"Building SkillMetadata programmatically via the builder without calling skillPath(...), or a loader that fails to resolve the skill directory before invoking build().","commonSituations":"Manually constructing SkillMetadata for a custom skill source and forgetting skillPath; a custom loader resolving SKILL.md without recording its parent directory.","solutions":["Call .skillPath(\"/absolute/path/to/skill\") on the builder before build()","If loading dynamically, resolve the SKILL.md file's absolute path and pass it in","Use the library's built-in loaders (ClasspathSkillRegistry / file registry) instead of hand-building metadata"],"exampleFix":"// before\nSkillMetadata.builder().name(\"my-skill\").description(\"does X\").build();\n// after\nSkillMetadata.builder().name(\"my-skill\").description(\"does X\").skillPath(\"/skills/my-skill/SKILL.md\").build();","handlingStrategy":"validation","validationCode":"if (skillPath == null || skillPath.isBlank()) throw new IllegalArgumentException(\"skillPath must be set before build()\");","typeGuard":"boolean hasPath(SkillMetadata m) { return m.getSkillPath() != null && !m.getSkillPath().isEmpty(); }","tryCatchPattern":"try { metadata = builder.build(); } catch (IllegalStateException e) { log.error(\"Skill metadata incomplete: {}\", e.getMessage()); }","preventionTips":["Always chain .skillPath(...) in the builder before build()","Prefer built-in loaders over manual builder usage","Unit-test custom skill loading code paths"],"tags":["skill","validation","builder"],"backgroundTag":"missing-required-config-field","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"}