{"record":{"id":"c325eb2ef7dc0e09","repo":"siyuan-note/siyuan","slug":"marketplace-package-manifest-not-found-or-invalid","errorCode":null,"errorMessage":"marketplace package manifest not found or invalid","messagePattern":"marketplace package manifest not found or invalid","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/bazaar/install.go","lineNumber":179,"sourceCode":"\tdirs, err := os.ReadDir(unzipPath)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tsrcPath := unzipPath\n\tif 1 == len(dirs) && dirs[0].IsDir() {\n\t\tsrcPath = filepath.Join(unzipPath, dirs[0].Name())\n\t}\n\n\t// 校验下载包自身声明的名称与请求安装的包名一致，防止把其他包的内容写入指定目录\n\t// https://github.com/siyuan-note/siyuan/security/advisories/GHSA-rpx2-p6hp-x5gj\n\tjsonFileName, ok := packageManifestNames[pkgType]\n\tif !ok {\n\t\treturn errors.New(\"invalid marketplace package type\")\n\t}\n\tpkg, parseErr := ParsePackageJSON(filepath.Join(srcPath, jsonFileName))\n\tif parseErr != nil || nil == pkg {\n\t\treturn errors.New(\"marketplace package manifest not found or invalid\")\n\t}\n\tif packageName != pkg.Name {\n\t\treturn fmt.Errorf(\"marketplace package name mismatch: expected [%s], got [%s]\", packageName, pkg.Name)\n\t}\n\n\tif err = replacePackageDirectory(srcPath, installPath, update); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\n// replacePackageDirectory 将 sourcePath 整目录替换到 installPath。\n// 先拷到安装目录同级的 staging，更新时再把旧目录 rename 成 backup，最后把 staging rename 成目标路径。\n// 这样新包已删除的文件不会残留，失败时也可以把 backup rename 回去。\nfunc replacePackageDirectory(sourcePath, installPath string, update bool) (err error) {\n\tpackageInstallLock.Lock()\n\tdefer packageInstallLock.Unlock()\n","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/bazaar/install.go#L161-L197","documentation":"After extraction, installPackage parses the package's manifest (e.g. plugin.json / theme.json per packageManifestNames). If the manifest file is missing, unreadable, or invalid JSON, the install is aborted rather than installing unverified content.","triggerScenarios":"The downloaded archive does not contain the expected manifest at its root, or the manifest fails ParsePackageJSON (malformed JSON, wrong schema, empty result).","commonSituations":"Package zip built with an extra top-level folder so the manifest isn't at srcPath root; author released a broken package; truncated/corrupted download; a package type whose archive layout changed across versions.","solutions":["Fix the package archive so the manifest (plugin.json/theme.json/etc.) sits at the archive root and is valid JSON","Validate the manifest locally (jq / JSON schema check) before publishing","Re-download the package — the artifact may be truncated; verify archive integrity","Check that the package type matches the manifest kind actually shipped"],"exampleFix":"// before: zip contains my-plugin/dist/... with plugin.json nested in my-plugin/\n// after: repackage so plugin.json is at the archive root","handlingStrategy":"validation","validationCode":"mf, err := f.ReadFile(\"plugin.json\")\nif err != nil { return fmt.Errorf(\"archive missing plugin.json\") }\nvar pkg PluginJSON\nif err := json.Unmarshal(mf, &pkg); err != nil || pkg.Name == \"\" {\n    return fmt.Errorf(\"plugin.json invalid\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Package archives with the manifest at the archive root","Validate manifest JSON in CI before publishing a package","Re-download if the artifact might be truncated"],"tags":["bazaar","json","manifest","validation"],"backgroundTag":"json-parse-error","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}