siyuan-note/siyuan · critical
panic: gulu.Zip.Unzip error (dynamic message from err)
Error message
panic: gulu.Zip.Unzip error (dynamic message from err)
What it means
The HarmonyOS gomobile export `Unzip` calls `gulu.Zip.Unzip(zipPath, destination)` and, on any non-nil error, calls `panic(err)` after logging. Because exported gomobile functions cannot return Go errors to the native side, the implementation converts failure into a panic that crashes the Harmony runtime. The panic message is dynamic — it is whatever `err` from `gulu.Zip.Unzip` contained.
Source
Thrown at kernel/harmony/kernel.go:241
return C.CString(util.FilterUploadFileName(C.GoString(name)))
}
//export AssetName
func AssetName(name *C.char) *C.char {
return C.CString(util.AssetName(C.GoString(name), ast.NewNodeID()))
}
//export HTML2Markdown
func HTML2Markdown(html *C.char) *C.char {
return C.CString(util.NewLute().HTML2Md(C.GoString(html)))
}
//export Unzip
func Unzip(zipFilePath, destination *C.char) {
var zipPath string = C.GoString(zipFilePath)
if err := gulu.Zip.Unzip(zipPath, C.GoString(destination)); nil != err {
logging.LogErrorf("unzip [%s] failed: %s", zipPath, err)
panic(err)
}
}
//export GetExportFilePath
func GetExportFilePath(exportPath *C.char) *C.char {
pathStr := C.GoString(exportPath)
var absPath string
if strings.HasPrefix(pathStr, "/export/") {
fileName := strings.TrimPrefix(pathStr, "/export/")
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]", pathStr, fileName)
return nil
}
// 加密导出需要持有覆盖原生复制过程的租约,旧路径解析接口不再返回其明文地址。View on GitHub (pinned to 251596fc0d)
Solutions
- Verify the source zip exists and is a valid archive before calling `Unzip` from the native side.
- Ensure the destination directory is created and writable by the app sandbox.
- Wrap the native call in a Harmony-level try/catch and surface a user-visible error instead of crashing.
- Pre-validate with a lightweight zip-header check to avoid triggering the panic.
Example fix
// Native caller (ArkTS/Java side) must guard, since Go side panics:
try {
Unzip(zipPath, destPath)
} catch (e) {
// show error to user; do not crash the UI
} Defensive patterns
Strategy: try-catch
Validate before calling
// On the native side, validate inputs before calling the Go export:
if (!fs.existsSync(zipPath) || !fs.existsSync(destDir)) {
throw new Error('zip source or destination missing')
} Try / catch
// Harmony/ArkTS caller must catch the Go panic surfaced as an exception:
try {
Unzip(zipPath, destPath)
} catch (e) {
// log and present a user-facing error; keep the app alive
logger.error(`unzip failed: ${e}`)
} Prevention
- Validate the zip path exists and the dest dir is writable before invoking Unzip.
- Never let the panic propagate to the UI thread uncaught.
- Pre-check zip headers to fail gracefully instead of panicking.
When it happens
Trigger: The zip path does not exist, is not a valid zip, is unreadable, the destination directory does not exist or is unwritable, or the archive is corrupt. Any of these makes `gulu.Zip.Unzip` return an error, which is then panicked.
Common situations: Importing a downloaded template/plugin pack that failed to fully download; unzipping into a sandboxed Harmony path without permission; a truncated asset archive shipped with an update.
Related errors
- unzip [%s] failed: %s
- 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/129b2c99836172b3.
Report an issue: GitHub.