siyuan-note/siyuan · error

marketplace package manifest not found or invalid

Error message

marketplace package manifest not found or invalid

What it means

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.

Solutions

  1. Fix the package archive so the manifest (plugin.json/theme.json/etc.) sits at the archive root and is valid JSON
  2. Validate the manifest locally (jq / JSON schema check) before publishing
  3. Re-download the package — the artifact may be truncated; verify archive integrity
  4. Check that the package type matches the manifest kind actually shipped

Example fix

// before: zip contains my-plugin/dist/... with plugin.json nested in my-plugin/
// after: repackage so plugin.json is at the archive root
Defensive patterns

Strategy: validation

Validate before calling

mf, err := f.ReadFile("plugin.json")
if err != nil { return fmt.Errorf("archive missing plugin.json") }
var pkg PluginJSON
if err := json.Unmarshal(mf, &pkg); err != nil || pkg.Name == "" {
    return fmt.Errorf("plugin.json invalid")
}

Prevention

When it happens

Trigger: The downloaded archive does not contain the expected manifest at its root, or the manifest fails ParsePackageJSON (malformed JSON, wrong schema, empty result).

Common situations: 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.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/c325eb2ef7dc0e09. Report an issue: GitHub.

Appendix: source

Thrown at kernel/bazaar/install.go:179

	dirs, err := os.ReadDir(unzipPath)
	if err != nil {
		return
	}

	srcPath := unzipPath
	if 1 == len(dirs) && dirs[0].IsDir() {
		srcPath = filepath.Join(unzipPath, dirs[0].Name())
	}

	// 校验下载包自身声明的名称与请求安装的包名一致,防止把其他包的内容写入指定目录
	// https://github.com/siyuan-note/siyuan/security/advisories/GHSA-rpx2-p6hp-x5gj
	jsonFileName, ok := packageManifestNames[pkgType]
	if !ok {
		return errors.New("invalid marketplace package type")
	}
	pkg, parseErr := ParsePackageJSON(filepath.Join(srcPath, jsonFileName))
	if parseErr != nil || nil == pkg {
		return errors.New("marketplace package manifest not found or invalid")
	}
	if packageName != pkg.Name {
		return fmt.Errorf("marketplace package name mismatch: expected [%s], got [%s]", packageName, pkg.Name)
	}

	if err = replacePackageDirectory(srcPath, installPath, update); err != nil {
		return
	}
	return
}

// replacePackageDirectory 将 sourcePath 整目录替换到 installPath。
// 先拷到安装目录同级的 staging,更新时再把旧目录 rename 成 backup,最后把 staging rename 成目标路径。
// 这样新包已删除的文件不会残留,失败时也可以把 backup rename 回去。
func replacePackageDirectory(sourcePath, installPath string, update bool) (err error) {
	packageInstallLock.Lock()
	defer packageInstallLock.Unlock()

View on GitHub (pinned to 9f775e8a12)