alibaba/spring-ai-alibaba · error · IOException

SKILL.md not found at: {}

Error message

SKILL.md not found at: {}

What it means

SkillMetadata.loadFullContent lazily reads SKILL.md from the skill's directory and caches it. If the file does not exist at skillPath/SKILL.md, an IOException is thrown because a skill is defined by its SKILL.md file and cannot be loaded without it.

Source

Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/skills/SkillMetadata.java:98

	}

	public String getFullContent() {
		return fullContent;
	}

	public List<String> getAllowedTools() {
		return allowedTools;
	}

	public void setAllowedTools(List<String> allowedTools) {
		this.allowedTools = allowedTools == null ? List.of() : List.copyOf(allowedTools);
	}

	public String loadFullContent() throws IOException {
		if (fullContent == null) {
			Path skillFile = Path.of(skillPath, "SKILL.md");
			if (!Files.exists(skillFile)) {
				throw new IOException("SKILL.md not found at: " + skillFile);
			}

			String rawContent = Files.readString(skillFile);
			fullContent = removeFrontmatter(rawContent);
		}
		return fullContent;
	}

	private String removeFrontmatter(String content) {
		if (!content.startsWith("---")) {
			return content;
		}

		int endIndex = content.indexOf("---", 3);
		if (endIndex == -1) {
			return content;
		}

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Verify Files.exists(Path.of(skillPath, "SKILL.md")) before loading and fix skillPath to point at the skill directory
  2. Check for case-sensitive filename mismatches (must be exactly SKILL.md)
  3. If packaging resources, ensure SKILL.md is copied into the built artifact (Maven resources include)
  4. Use an absolute path or resolve relative paths against the correct base directory at runtime

Example fix

// before
SkillMetadata meta = SkillMetadata.builder().name("x").skillPath("skills/x/SKILL.md")...build();
// after: point at the directory containing SKILL.md
SkillMetadata meta = SkillMetadata.builder().name("x").skillPath("skills/x")...build();
Defensive patterns

Strategy: validation

Validate before calling

Path f = Path.of(skillPath, "SKILL.md");
if (!Files.isRegularFile(f)) throw new IllegalArgumentException("Missing SKILL.md at " + f);

Type guard

boolean hasSkillManifest(String skillPath) {
    return Files.isRegularFile(Path.of(skillPath, "SKILL.md"));
}

Try / catch

try { content = skill.loadFullContent(); } catch (IOException e) { if (e.getMessage().startsWith("SKILL.md not found")) { content = fallbackContent(skill.getName()); } else throw e; }

Prevention

When it happens

Trigger: Calling loadFullContent (directly or via readSkillContent/readSkillContentByPath) when the directory at skillPath lacks a SKILL.md file — wrong path, missing packaging, or path pointing at the file itself instead of its directory.

Common situations: Deployed jar missing skill resource directories; skill folder copied without SKILL.md; wrong casing (skill.md) on case-sensitive filesystems; path configured relative to a working directory different from runtime.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09). Data as JSON: /api/errors/f4d70aebedfb13c1. Report an issue: GitHub.