siyuan-note/siyuan · error
marketplace package manifest must be at the archive root or
Error message
marketplace package manifest must be at the archive root or its only top-level directory
What it means
localPackageRoot could not find a recognized manifest at the extraction root, and the root does not contain exactly one top-level directory entry. Thrown at kernel/bazaar/local.go:185-186 when len(entries) != 1 or the single entry is not a directory. Packages must lay out as either manifest-at-root or a single wrapping directory.
Source
Thrown at kernel/bazaar/local.go:186
return errors.New("marketplace package contains a file that is too large")
}
if uint64(written) > maxLocalPackageExtractSize-*extractedTotal {
return errors.New("marketplace package is too large")
}
*extractedTotal += uint64(written)
return nil
}
func localPackageRoot(extractPath string) (string, error) {
if hasLocalPackageManifest(extractPath) {
return extractPath, nil
}
entries, err := os.ReadDir(extractPath)
if err != nil {
return "", err
}
if len(entries) != 1 || !entries[0].IsDir() {
return "", errors.New("marketplace package manifest must be at the archive root or its only top-level directory")
}
root := filepath.Join(extractPath, entries[0].Name())
if !hasLocalPackageManifest(root) {
return "", errors.New("marketplace package manifest not found")
}
return root, nil
}
func hasLocalPackageManifest(root string) bool {
for manifestName := range localPackageManifests {
if info, err := os.Stat(filepath.Join(root, manifestName)); err == nil && info.Mode().IsRegular() {
return true
}
}
return false
}
View on GitHub (pinned to 251596fc0d)
Solutions
- Restructure the archive so the manifest is either at the zip root or inside exactly one top-level directory
- Remove sibling top-level entries (README at root is fine only if there is a single wrapping dir)
- Re-zip from the correct level so the layout is root/manifest.json or root/<pkgdir>/manifest.json
Example fix
# before: two top-level entries, manifest inside one of them myplugin/ README.md # -> fails: len(entries) == 2 # after: single top-level dir holding manifest + README myplugin/plugin.json myplugin/README.md
Defensive patterns
Strategy: validation
Validate before calling
// After test-extraction, confirm layout is manifest-at-root or single wrapping dir
func assertLayout(extractPath string) error {
if hasManifest(extractPath) { return nil }
es, err := os.ReadDir(extractPath)
if err != nil { return err }
if len(es) != 1 || !es[0].IsDir() {
return errors.New("need manifest at root or exactly one top-level dir")
}
return nil
} Try / catch
if _, err := localPackageRoot(tempPath); err != nil {
cleanup()
return fmt.Errorf("package layout rejected: %w", err)
} Prevention
- Standardize on one of two layouts: manifest at zip root, or a single wrapping directory
- Avoid mixing top-level files and sibling directories in the archive
- Document the expected layout in the package author guide
When it happens
Trigger: After extraction, the extract root has zero, two, or more top-level entries, or a single non-directory entry, and none of the recognized manifests (plugin.json/theme.json/icon.json/template.json/widget.json) sits directly at the root.
Common situations: The author zipped the contents flat (manifest buried in a subdir) alongside sibling folders; the archive wraps multiple top-level projects; the manifest sits one level too deep or too shallow.
Related errors
- invalid marketplace package archive
- marketplace package archive is empty
- marketplace package manifest not found or invalid
- marketplace package name mismatch: expected [%s], got [%s]
- multiple marketplace package manifests found
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/b3a6f199646d18d8.
Report an issue: GitHub.