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
- Download the URL manually and confirm it is a valid zip (unzip it locally).
- If the source is a tar.gz, switch to a zip URL or a raw SKILL.md link.
- Re-run the install to rule out a truncated download.
- 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
- Confirm the URL actually serves a zip by downloading it locally first.
- Use standard zip tooling; avoid unsupported compression variants.
- Prefer raw SKILL.md links when only a single file is needed.
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
- invalid marketplace package archive
- marketplace package archive is empty
- marketplace package contains too many files
- marketplace package contains a file that is too large
- marketplace package is too large
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/1b2be984af192476.
Report an issue: GitHub.