siyuan-note/siyuan · warning

skill source is required

Error message

skill source is required

What it means

Thrown by InstallSkill at the very top when rawURL is empty after trimming whitespace. It is the precondition guard ensuring a non-empty source string before any URL normalization or network call. Pure input validation, no I/O involved.

Source

Thrown at kernel/util/skill.go:253

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

Solutions

  1. Validate non-empty input in the API/UI handler before calling InstallSkill and return a clear field error.
  2. Trim and check the string client-side; disable the install button when empty.
  3. Default to a documented example URL in test scaffolding rather than an empty string.

Example fix

// before
res, err := util.InstallSkill(strings.TrimSpace(input))

// after
src := strings.TrimSpace(input)
if src == "" {
    return errors.New("please provide a skill source URL or owner/repo")
}
res, err := util.InstallSkill(src)
Defensive patterns

Strategy: validation

Validate before calling

src := strings.TrimSpace(rawURL)
if src == "" {
    return errors.New("a skill source URL or owner/repo is required")
}

Prevention

When it happens

Trigger: Calling InstallSkill with an empty string, only whitespace, or a value that became empty after TrimSpace. Typically an API handler or UI that forwarded an unvalidated empty field.

Common situations: The install dialog was submitted without a URL; a programmatic caller passed a zero-value string; an upstream trim already cleared the field but the caller did not short-circuit.

Related errors


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