siyuan-note/siyuan · critical
unzip [%s] failed: %s
Error message
unzip [%s] failed: %s
What it means
Logged via logging.LogErrorf and then re-panicked by the mobile kernel's Unzip helper when gulu.Zip.Unzip fails to extract zipFilePath into destination. This is a gomobile-exported function called from native Android/iOS/HarmonyOS code; because gomobile bindings cannot return Go errors ergonomically, the helper panics after logging so the native layer receives a recoverable crash. The %s in the log line interpolates the underlying error.
Source
Thrown at kernel/mobile/kernel.go:371
return filepath.Base(path)
}
func FilterUploadFileName(name string) string {
return util.FilterUploadFileName(name)
}
func AssetName(name string) string {
return util.AssetName(name, ast.NewNodeID())
}
func HTML2Markdown(html string) string {
return util.NewLute().HTML2Md(html)
}
func Unzip(zipFilePath, destination string) {
if err := gulu.Zip.Unzip(zipFilePath, destination); nil != err {
logging.LogErrorf("unzip [%s] failed: %s", zipFilePath, err)
panic(err)
}
}
// GetExportFilePath 解析导出文件绝对路径,绕过 HTTP 层以避免锁屏密码拦截。
// exportPath 格式为 "/export/xxx.zip" 或 "assets/xxx"。
// 返回文件在磁盘上的绝对路径,以便原生端分块拷贝,避免大文件内存溢出。
// 解析失败返回空字符串。
func GetExportFilePath(exportPath string) (ret string) {
var absPath string
if after, ok := strings.CutPrefix(exportPath, "/export/"); ok {
fileName := after
if decoded, err := url.PathUnescape(fileName); err == nil {
fileName = decoded
}
fileName = filepath.Clean(fileName)
if strings.HasPrefix(fileName, "..") {
logging.LogWarnf("get export file path [%s] blocked: path traversal attempt [%s]", exportPath, fileName)
returnView on GitHub (pinned to 251596fc0d)
Solutions
- Verify zipFilePath exists and is readable before calling Unzip; ensure the download completed (check size/checksum).
- Create the destination directory (os.MkdirAll) with appropriate permissions before calling Unzip.
- Confirm the process has write permission to destination (especially on Android scoped storage).
- If the file may be corrupt, re-download it; validate with a zip integrity check before invoking Unzip.
Example fix
// before (native side): unzip whatever path was passed
kernel.Unzip(zipPath, dest)
// after: native side pre-checks, and kernel helper could be wrapped
// In Go, if you control the call site, prefer returning an error instead of panicking:
func Unzip(zipFilePath, destination string) error {
if err := os.MkdirAll(destination, 0755); err != nil { return err }
return gulu.Zip.Unzip(zipFilePath, destination)
} Defensive patterns
Strategy: try-catch
Validate before calling
if _, err := os.Stat(zipFilePath); err != nil { return err }
if err := os.MkdirAll(destination, 0755); err != nil { return err } Try / catch
defer func() {
if r := recover(); r != nil {
// log r, surface a user-facing error, keep the app alive
}
}()
kernel.Unzip(zipPath, dest) Prevention
- Pre-create and permission the destination directory before unzipping.
- Verify the archive integrity (size, CRC) after download and before extraction.
- On native platforms, wrap Unzip calls in recover to convert panics into recoverable errors.
When it happens
Trigger: Native code calls kernel.Unzip with a path that does not exist, is not a valid zip, is corrupted/truncated, or whose destination is not writable / does not exist / is on a protected storage path.
Common situations: Downloading a marketplace package (theme/icon/template/plugin) whose download was truncated; a destination directory that was not created first; permission denied on external storage; a file that is a zip64 archive the unzip lib cannot handle; disk full mid-extraction.
Related errors
- panic: gulu.Zip.Unzip error (dynamic message from err)
- invalid marketplace package archive
- marketplace package archive is empty
- marketplace package contains too many files
- marketplace package contains a file that is too large
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/a79466e34d42c4cc.
Report an issue: GitHub.