siyuan-note/siyuan · error
no valid skill installed
Error message
no valid skill installed
What it means
installSkillDirs unzips candidate skill directories and copies each one containing a valid SKILL.md into the workspace skills directory. When every candidate is skipped (unreadable SKILL.md, missing or invalid 'name' frontmatter), result.Names stays empty and the installer fails with 'no valid skill installed' rather than succeeding silently.
Solutions
- Open the zip and add a valid 'name: <skill-name>' field to the YAML frontmatter of every SKILL.md
- Ensure SKILL.md sits in a subdirectory (not archive root) if there is no name field, so the directory name can be used as fallback
- Verify each skill name passes validateSkillName (valid identifier-like characters)
- Regenerate/re-download the archive and confirm SKILL.md files are readable and non-empty
Example fix
// before --- description: My skill --- // after --- name: my-skill description: My skill ---
Defensive patterns
Strategy: validation
Validate before calling
const md = await zip.file('**/SKILL.md').async('string');
const fm = md.match(/^---\n([\s\S]*?)\n---/);
if (!fm || !/^name:\s*\S+/m.test(fm[1])) throw new Error('archive SKILL.md missing name frontmatter'); Try / catch
try {
await api.installSkill(zipData);
} catch (e) {
if (e.message.includes('no valid skill installed')) {
// inspect archive SKILL.md frontmatter and fix before retrying
}
} Prevention
- Always include a 'name' field in SKILL.md frontmatter when packaging zips
- Test-install every archive you distribute
- Keep skill names within validateSkillName rules
- Avoid shipping raw markdown collections as skill archives
When it happens
Trigger: Calling InstallSkill with a zip whose SKILL.md files are all unreadable, all lack a 'name' frontmatter field at archive root, or all have names rejected by validateSkillName.
Common situations: Installing a skill archive exported without frontmatter; a zip with a single SKILL.md at the root missing 'name'; an archive of plain markdown notes; corrupted zip entries; name validation failures after renaming skills.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- SKILL.md frontmatter missing 'name' field
- all asset mappings failed
- assetPath is not an image referenced by the document
- assetPath is required for analyze
- at least one asset path or mapping is required
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/0ef05f07f68da7b1.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/util/skill.go:846
if err := os.MkdirAll(SkillsDir(), 0755); err != nil {
return nil, err
}
// 覆盖式安装:先清旧目录
if gulu.File.IsExist(destDir) {
os.RemoveAll(destDir)
}
if err := filelock.Copy(srcDir, destDir); err != nil {
return nil, fmt.Errorf("install skill %s failed: %s", name, err)
}
result.Names = append(result.Names, name)
desc := fm["description"]
if desc == "" {
desc = firstLine(body)
}
result.Descriptions = append(result.Descriptions, desc)
}
if len(result.Names) == 0 {
return nil, errors.New("no valid skill installed")
}
return result, nil
}
// installFromSingleSkillMD 把单个 SKILL.md 文本内容落地为一个 skill
func installFromSingleSkillMD(data []byte) (*InstallSkillResult, error) {
content := string(data)
fm, body := parseSkillFrontmatter(content)
name := fm["name"]
if name == "" {
return nil, errors.New("SKILL.md frontmatter missing 'name' field")
}
if err := validateSkillName(name); err != nil {
return nil, err
}
if err := SaveSkill(name, content); err != nil {
return nil, err
}View on GitHub (pinned to 9f775e8a12)