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

  1. Open the zip and add a valid 'name: <skill-name>' field to the YAML frontmatter of every SKILL.md
  2. 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
  3. Verify each skill name passes validateSkillName (valid identifier-like characters)
  4. 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

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


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)