siyuan-note/siyuan · error
skill source is required
Error message
skill source is required
What it means
InstallSkill downloads and installs a skill from a GitHub repo, raw SKILL.md link, or release zip. It rejects an empty/whitespace-only source string with 'skill source is required' before any URL normalization is attempted.
Solutions
- Provide a non-empty source: an owner/repo shorthand, a GitHub URL, or a raw SKILL.md/zip URL
- Validate the input is non-empty in the UI/form before invoking the install
- Check that the config value supplying the source is populated
Example fix
// before
InstallSkill(cfg.SkillSource) // may be ""
// after
if strings.TrimSpace(cfg.SkillSource) == "" {
return errors.New("skill source must be set")
}
result, err := InstallSkill(cfg.SkillSource) Defensive patterns
Strategy: validation
Validate before calling
const source = (input ?? "").trim();
if (!source) throw new Error("skill source is required"); Try / catch
if (!input || !input.trim()) {
return { ok: false, reason: "empty skill source" };
}
try {
await installSkill(input);
} catch (e) {
if (String(e.message) === "skill source is required") {
// prompt the user for a source
}
} Prevention
- Validate install-form inputs before submission
- Check that config values feeding the source are non-empty
- Trim whitespace from pasted sources
When it happens
Trigger: Calling InstallSkill("") or InstallSkill(" ") — a blank source string after TrimSpace.
Common situations: Empty input field in the install dialog; variable not assigned before the call; config value for skill source missing or blank; form submitted without filling the source.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- AI editor input is empty
- asset data is empty
- at least one asset path or mapping is required
- Conf.Language(294)
- createDocTree document list must not be empty
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/257c6a1f1d9c060a.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/util/skill.go:555
var ownerRepoPattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9._-]*/[A-Za-z0-9][A-Za-z0-9._-]*$`)
// skillsAddPattern 从 "npx skills add owner/repo ..." 这类命令里提取 owner/repo
var skillsAddPattern = regexp.MustCompile(`(?:^|\s)([A-Za-z0-9][A-Za-z0-9._-]*/[A-Za-z0-9][A-Za-z0-9._-]*)(?:\s|$)`)
// normalizedSkillSource 描述归一化后的下载源
type normalizedSkillSource struct {
downloadURL string // 实际 GET 的地址
isZip bool // 是否按 zip 解压处理(codeload / release zip / Content-Type 判定为 zip)
branch string // codeload 分支,空表示无需回退;main 失败回退 master
}
// InstallSkill 从 GitHub 仓库或直链下载并安装 skill 到 SkillsDir()。
// 支持的输入:owner/repo 简写、整条 "npx skills add owner/repo -g" 命令、
// 完整 GitHub 仓库/子目录/commit URL、raw SKILL.md 直链、release zip 直链。
func InstallSkill(rawURL string) (*InstallSkillResult, error) {
rawURL = strings.TrimSpace(rawURL)
if rawURL == "" {
return nil, errors.New("skill source is required")
}
src, err := normalizeSkillURL(rawURL)
if err != nil {
return nil, err
}
data, contentType, err := downloadSkillSource(src)
if err != nil {
return nil, err
}
// 按内容类型或来源判定处理方式
isZip := src.isZip || strings.HasPrefix(contentType, "application/zip") ||
strings.HasPrefix(contentType, "application/x-zip-compressed")
if isZip {
return installFromZip(data)View on GitHub (pinned to 9f775e8a12)