siyuan-note/siyuan · error

no valid skill installed

Error message

no valid skill installed

What it means

Thrown by installSkillDirs when every candidate skill in the archive was skipped, leaving zero installed. Skips happen when SKILL.md cannot be read, the frontmatter lacks 'name' (root case), or the name fails validation. The loop logs each skip but ultimately fails the install.

Source

Thrown at kernel/util/skill.go:542

		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 251596fc0d)

Solutions

  1. Add a 'name:' field to the frontmatter of each SKILL.md, especially any at the archive root.
  2. Place each skill in a named subdirectory so the directory name can backstop a missing frontmatter name.
  3. Ensure skill names pass validateSkillName (no slashes, traversal, or '.'/'..' literals).

Example fix

// before — SKILL.md at archive root with no name
---
description: a skill
---
...

// after — add the name field
---
name: my-skill
description: a skill
---
...
Defensive patterns

Strategy: validation

Validate before calling

// validate each SKILL.md in the source has a usable name before packaging
for _, md := range skillFiles {
    fm, _ := parseFrontmatter(md)
    if fm["name"] == "" && !hasParentDir(md) {
        return errors.New("root SKILL.md must declare a 'name' frontmatter field")
    }
}

Prevention

When it happens

Trigger: An archive whose only SKILL.md sits at the root with no 'name' frontmatter (skipped because the root temp dir name cannot be used); all skills have invalid names (slashes, traversal); SKILL.md files are unreadable.

Common situations: A root-level SKILL.md missing the 'name:' field; skill directories named with characters validateSkillName rejects; permission errors reading all SKILL.md files in the zip.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/0ef05f07f68da7b1. Report an issue: GitHub.