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

  1. 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)
  2. Find symlink entries with `unzip -l pkg.zip` (or `unzip -Z1` plus inspection) and remove or replace them in the source tree
  3. If a symlink is required for distribution, ship the real file content instead — SiYuan marketplace packages must be self-contained
  4. 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

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


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)