siyuan-note/siyuan · error
unrecognized skill source: %s
Error message
unrecognized skill source: %s
What it means
Thrown by normalizeSkillURL when the input is neither an 'npx skills add' command, an owner/repo shorthand, nor a parseable URL with scheme and host. It is the catch-all rejection for inputs that do not match any supported source format.
Source
Thrown at kernel/util/skill.go:301
func normalizeSkillURL(raw string) (normalizedSkillSource, error) {
raw = strings.TrimSpace(raw)
// 1. 整条 "npx skills add owner/repo ..." 命令:提取 owner/repo
if strings.Contains(raw, "skills add") || strings.Contains(raw, "skills@") {
if m := skillsAddPattern.FindStringSubmatch(raw); len(m) == 2 {
return codeloadSource(m[1], "main"), nil
}
}
// 2. owner/repo 简写(无 scheme、无点、单个 /)
if !strings.Contains(raw, "://") && !strings.Contains(raw, "//") && ownerRepoPattern.MatchString(raw) {
return codeloadSource(raw, "main"), nil
}
// 3. 带 scheme 的 URL
u, err := url.Parse(raw)
if err != nil || u.Scheme == "" || u.Host == "" {
return normalizedSkillSource{}, fmt.Errorf("unrecognized skill source: %s", raw)
}
switch u.Host {
case "github.com":
return normalizeGitHubURL(u)
case "raw.githubusercontent.com":
// 直接 GET 单个 SKILL.md(或其它文本文件)
return normalizedSkillSource{downloadURL: u.String()}, nil
default:
// 其它直链(release zip、自建站点等):直接 GET,是否 zip 交由 Content-Type 判定
return normalizedSkillSource{downloadURL: u.String()}, nil
}
}
// codeloadSource 构造 codeload zip 下载源,branch 用于 main→master 回退
func codeloadSource(ownerRepo, branch string) normalizedSkillSource {
return normalizedSkillSource{
downloadURL: "https://codeload.github.com/" + ownerRepo + "/zip/refs/heads/" + branch,View on GitHub (pinned to 251596fc0d)
Solutions
- Provide a full URL starting with https://, an owner/repo shorthand, or the full 'npx skills add owner/repo' command.
- Ensure owner/repo shorthand has no scheme, no '//' and exactly one slash with no dots in either segment.
- Validate and pre-format the input in the UI before submission.
Example fix
// before
util.InstallSkill("myskill")
// after — valid shorthand or full URL
util.InstallSkill("owner/repo")
// or
util.InstallSkill("https://github.com/owner/repo") Defensive patterns
Strategy: validation
Validate before calling
import "net/url"
func isValidSkillSource(raw string) bool {
raw = strings.TrimSpace(raw)
if raw == "" {
return false
}
if strings.Contains(raw, "skills add") || strings.Contains(raw, "skills@") {
return true
}
if !strings.Contains(raw, "://") && !strings.Contains(raw, "//") && ownerRepoPattern.MatchString(raw) {
return true
}
u, err := url.Parse(raw)
return err == nil && u.Scheme != "" && u.Host != ""
} Prevention
- Document the three accepted input shapes to users (shorthand, npx command, full URL).
- Pre-validate with url.Parse and report the parse error verbatim.
- Avoid pasting URLs with leading/trailing whitespace or smart quotes.
When it happens
Trigger: Passing a bare string with no scheme (e.g. 'myskill'), a malformed URL (e.g. 'htp://example'), a URL with empty host, or a value containing '://' that url.Parse rejects. The owner/repo regex also requires no dots and exactly one slash, so 'example.com/x' bypasses it.
Common situations: User typed a skill name instead of a source; missing https:// prefix; copy-paste introduced a stray character; the shorthand regex rejected input containing dots or multiple slashes.
Related errors
- skill source is required
- Activation code cannot be empty
- invalid notebook ID
- OIDC claim rule values cannot be empty
- Conf.Language(142)
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/0389996b8091dbfa.
Report an issue: GitHub.