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

  1. Point the install at a raw SKILL.md (raw.githubusercontent.com) or a true .zip archive URL.
  2. If serving your own skill, ensure the server emits a correct Content-Type (text/plain or application/zip).
  3. 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

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


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