siyuan-note/siyuan · error
marketplace package contains an unsupported file
Error message
marketplace package contains an unsupported file
What it means
This error is thrown by extractLocalPackageItem when a zip entry of a locally uploaded marketplace package is neither a regular file nor a directory — typically a symlink (mode&os.ModeSymlink != 0) or a special file (device, fifo, etc.). The library refuses to extract such entries so a package cannot plant symlinks pointing outside the extraction directory or other dangerous node types.
Solutions
- Rebuild the archive without preserving symlinks so linked content is stored as regular files: `zip -r pkg.zip .` without the -y flag (or replace symlinks with real copies first)
- Find symlink entries with `unzip -l pkg.zip` (or `unzip -Z1` plus inspection) and remove or replace them in the source tree
- If a symlink is required for distribution, ship the real file content instead — SiYuan marketplace packages must be self-contained
- If the archive is third-party, request a repackaged version containing only regular files and directories
Example fix
// before (shell packaging preserving symlinks) zip -ry plugin.zip . // after rsync -rL --exclude node_modules ./ /tmp/stage/ (cd /tmp/stage && zip -r ../plugin.zip .)
Defensive patterns
Strategy: validation
Validate before calling
// Verify no symlinks/irregular entries before upload (Go helper)
func archiveHasOnlyRegularEntries(path string) bool {
r, err := zip.OpenReader(path)
if err != nil { return false }
defer r.Close()
for _, f := range r.File {
m := f.Mode()
if m&os.ModeSymlink != 0 || (!m.IsRegular() && !m.IsDir()) {
return false
}
}
return true
} Try / catch
if err := extractPackage(zipPath); err != nil {
if strings.Contains(err.Error(), "unsupported file") {
// instruct user to repackage without symlinks
}
} Prevention
- Avoid `zip -y` when packaging; let symlinks be stored as real content
- Replace project symlinks with copies before packaging
- Check the source tree (`find . -type l`) before building the archive
When it happens
Trigger: Calling ExtractLocalPackage(archivePath) with a zip that stores symlinks (common when built with `zip -y` on a project containing symlinks, or with GNU tar-converted zips) or entries with irregular modes such as setuid special files.
Common situations: Packaging a plugin/theme on Linux/macOS where the project contains symlinks (e.g. node_modules links, shared assets) and the zip tool preserved them; archives produced by non-zip tools with unusual entry types; malicious packages attempting symlink attacks.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- archive entry resolves outside destination
- marketplace package manifest must be at the archive root or…
- notebook asset path resolves outside notebook directory
- resource escapes the data directory
- symlink [ ] resolves outside data/assets: [ ]
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/b494aee1017ae500.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/bazaar/local.go:141
return err
}
}
return nil
}
func extractLocalPackageItem(item *zip.File, destination string, extractedTotal *uint64) error {
name := strings.ReplaceAll(item.Name, "\\", "/")
if name == "" || strings.HasPrefix(name, "/") {
return errors.New("marketplace package contains an invalid path")
}
destinationPath := filepath.Join(destination, filepath.FromSlash(name))
if !gulu.File.IsSubPath(destination, destinationPath) {
return errors.New("marketplace package contains an invalid path")
}
mode := item.Mode()
if mode&os.ModeSymlink != 0 || (!mode.IsRegular() && !mode.IsDir()) {
return errors.New("marketplace package contains an unsupported file")
}
if mode.IsDir() {
return os.MkdirAll(destinationPath, 0755)
}
if err := os.MkdirAll(filepath.Dir(destinationPath), 0755); err != nil {
return err
}
source, err := item.Open()
if err != nil {
return err
}
defer source.Close()
target, err := os.OpenFile(destinationPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return err
}
written, copyErr := io.Copy(target, io.LimitReader(source, int64(maxLocalPackageFileSize)+1))View on GitHub (pinned to 9f775e8a12)