siyuan-note/siyuan · error
unsupported skill source (content-type: %s); expected a zip
Error message
unsupported skill source (content-type: %s); expected a zip archive or a SKILL.md text file
What it means
Thrown by InstallSkill after a successful download when the content-type is neither a zip (application/zip or application/x-zip-compressed) nor a text file (text/*), AND the body does not start with the '---' frontmatter delimiter. The installer refuses to guess how to handle the payload.
Source
Thrown at kernel/util/skill.go:279
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)
}
// 文本:当作单个 SKILL.md
if strings.HasPrefix(contentType, "text/") || strings.HasPrefix(strings.TrimSpace(string(data)), "---") {
return installFromSingleSkillMD(data)
}
return nil, fmt.Errorf("unsupported skill source (content-type: %s); expected a zip archive or a SKILL.md text file", contentType)
}
// normalizeSkillURL 把各种输入归一化为下载源
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
}
View on GitHub (pinned to 251596fc0d)
Solutions
- Point the install at a raw SKILL.md (raw.githubusercontent.com) or a true .zip archive URL.
- If serving your own skill, ensure the server emits a correct Content-Type (text/plain or application/zip).
- For single-file installs, ensure the file begins with '---' so the frontmatter fallback matches.
Example fix
// before — points to a JSON API endpoint
util.InstallSkill("https://api.github.com/repos/owner/repo/contents/SKILL.md")
// after — points to the raw file
util.InstallSkill("https://raw.githubusercontent.com/owner/repo/main/SKILL.md") Defensive patterns
Strategy: validation
Validate before calling
// prefer sources known to return a supported content-type
ok := strings.Contains(src, "raw.githubusercontent.com") || strings.HasSuffix(src, ".zip") || strings.Contains(src, "codeload.github.com")
if !ok {
// warn the user that the endpoint may be unsupported
} Prevention
- Prefer raw.githubusercontent.com for single-file skills and codeload/release zips for collections.
- If you host skills yourself, configure the server to emit text/plain or application/zip.
- For single-file skills, start the file with '---' so the frontmatter fallback engages.
When it happens
Trigger: The source returns a content-type like application/octet-stream, application/json, or image/* without a frontmatter marker; a misconfigured server omits a proper content-type; a raw file endpoint serves binary that is not a zip.
Common situations: GitHub API endpoints returning application/json instead of a raw file; a release asset that is a .tar.gz (not zip); CDN rewrites content-type; the URL actually points to an HTML error page served as text/html but the body got misread.
Related errors
- invalid download URL: %s
- download failed (tried main and master): %v
- download failed: %s
- download failed: HTTP %d
- read body failed: %s
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/42aef9e191a5be28.
Report an issue: GitHub.