{"record":{"id":"129b2c99836172b3","repo":"siyuan-note/siyuan","slug":"panic-gulu-zip-unzip-error-dynamic-message-from","errorCode":null,"errorMessage":"panic: gulu.Zip.Unzip error (dynamic message from err)","messagePattern":"panic: gulu\\.Zip\\.Unzip error \\(dynamic message from err\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"kernel/harmony/kernel.go","lineNumber":241,"sourceCode":"\treturn C.CString(util.FilterUploadFileName(C.GoString(name)))\n}\n\n//export AssetName\nfunc AssetName(name *C.char) *C.char {\n\treturn C.CString(util.AssetName(C.GoString(name), ast.NewNodeID()))\n}\n\n//export HTML2Markdown\nfunc HTML2Markdown(html *C.char) *C.char {\n\treturn C.CString(util.NewLute().HTML2Md(C.GoString(html)))\n}\n\n//export Unzip\nfunc Unzip(zipFilePath, destination *C.char) {\n\tvar zipPath string = C.GoString(zipFilePath)\n\tif err := gulu.Zip.Unzip(zipPath, C.GoString(destination)); nil != err {\n\t\tlogging.LogErrorf(\"unzip [%s] failed: %s\", zipPath, err)\n\t\tpanic(err)\n\t}\n}\n\n//export GetExportFilePath\nfunc GetExportFilePath(exportPath *C.char) *C.char {\n\tpathStr := C.GoString(exportPath)\n\tvar absPath string\n\tif strings.HasPrefix(pathStr, \"/export/\") {\n\t\tfileName := strings.TrimPrefix(pathStr, \"/export/\")\n\t\tif decoded, err := url.PathUnescape(fileName); err == nil {\n\t\t\tfileName = decoded\n\t\t}\n\t\tfileName = filepath.Clean(fileName)\n\t\tif strings.HasPrefix(fileName, \"..\") {\n\t\t\tlogging.LogWarnf(\"get export file path [%s] blocked: path traversal attempt [%s]\", pathStr, fileName)\n\t\t\treturn nil\n\t\t}\n\t\t// 加密导出需要持有覆盖原生复制过程的租约，旧路径解析接口不再返回其明文地址。","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/harmony/kernel.go#L223-L259","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// Native caller (ArkTS/Java side) must guard, since Go side panics:\ntry {\n  Unzip(zipPath, destPath)\n} catch (e) {\n  // show error to user; do not crash the UI\n}","handlingStrategy":"try-catch","validationCode":"// On the native side, validate inputs before calling the Go export:\nif (!fs.existsSync(zipPath) || !fs.existsSync(destDir)) {\n  throw new Error('zip source or destination missing')\n}","typeGuard":null,"tryCatchPattern":"// Harmony/ArkTS caller must catch the Go panic surfaced as an exception:\ntry {\n  Unzip(zipPath, destPath)\n} catch (e) {\n  // log and present a user-facing error; keep the app alive\n  logger.error(`unzip failed: ${e}`)\n}","preventionTips":["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."],"tags":["harmony","panic","zip","native","gomobile"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}