siyuan-note/siyuan · error

unzip failed: %s

Error message

unzip failed: %s

What it means

Thrown by installFromZip when gulu.Zip.Unzip fails to extract the downloaded zip. The downloaded bytes are written to a temp file and passed to gulu's unzip, which includes zip-slip protection. Failure means the archive is corrupt, not a zip, or contains unsafe entries.

Source

Thrown at kernel/util/skill.go:440

// installFromZip 解压 zip 并安装其中的 skill
func installFromZip(data []byte) (*InstallSkillResult, error) {
	tmpRoot := filepath.Join(TempDir, "ai", "skill-install", gulu.Rand.String(7))
	if err := os.MkdirAll(tmpRoot, 0755); err != nil {
		return nil, err
	}
	defer os.RemoveAll(tmpRoot)

	zipPath := filepath.Join(tmpRoot, "src.zip")
	if err := os.WriteFile(zipPath, data, 0644); err != nil {
		return nil, err
	}
	unzipDir := filepath.Join(tmpRoot, "unzip")
	if err := os.MkdirAll(unzipDir, 0755); err != nil {
		return nil, err
	}
	// gulu.Zip.Unzip 已内置 zip-slip 路径穿越防护
	if err := gulu.Zip.Unzip(zipPath, unzipDir); err != nil {
		return nil, errors.New("unzip failed: " + err.Error())
	}

	skillDirs := findSkillDirs(unzipDir)
	if len(skillDirs) == 0 {
		return nil, errors.New("no SKILL.md found in the archive")
	}
	return installSkillDirs(skillDirs, unzipDir)
}

// findSkillDirs 在解压根下查找含 SKILL.md 的 skill 目录,返回相对 root 的路径。
// 递归下钻以兼容任意包裹层(codeload 会把仓库内容包在 <repo-name>/ 下),
// 但一旦某个目录被认定为 skill(直接含 SKILL.md)就停止下钻,避免误入 skill 内部的
// references/scripts 等子目录。识别的结构:
//   - SKILL.md 直接在 root(无包裹)
//   - <wrap>/SKILL.md(单层或多层包裹的单 skill)
//   - <wrap>/skills/<name>/SKILL.md(集合仓库,wrap 可有可无)
func findSkillDirs(root string) []string {
	if gulu.File.IsExist(filepath.Join(root, "SKILL.md")) {

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Download the URL manually and confirm it is a valid zip (unzip it locally).
  2. If the source is a tar.gz, switch to a zip URL or a raw SKILL.md link.
  3. Re-run the install to rule out a truncated download.
  4. Provide a zip built with standard tools (avoid exotic compression formats).
Defensive patterns

Strategy: try-catch

Try / catch

res, err := util.InstallSkill(src)
if err != nil && strings.HasPrefix(err.Error(), "unzip failed") {
    // fall back to the raw SKILL.md URL if the zip is unusable
    res, err = util.InstallSkill(rawSkillMDURL)
}

Prevention

When it happens

Trigger: The downloaded payload is not actually a zip despite a zip-like content-type; the archive is truncated/corrupt; it contains entries with absolute or traversal paths that gulu rejects; an incomplete download produced a partial file.

Common situations: Content-type lied (server labeled a tar.gz or HTML as application/zip); a mid-stream truncation (see read-body failures) left a short file; the zip was created with unusual compression gulu cannot decode.

Related errors


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