siyuan-note/siyuan · error

marketplace package contains an unsupported file

Error message

marketplace package contains an unsupported file

What it means

A zip entry's mode is neither a regular file nor a directory (e.g. symlink, device, socket, named pipe). Thrown by extractLocalPackageItem (kernel/bazaar/local.go:139-141) which explicitly rejects os.ModeSymlink and any non-regular/non-dir mode. Marketplace packages may only ship regular files and directories.

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 251596fc0d)

Solutions

  1. Strip symlinks before zipping (resolve them to real files, or exclude them)
  2. Exclude symlink-heavy directories like node_modules, .cache, .bin from the archive
  3. Re-package using a tool that materializes symlinks into regular files

Example fix

# before: node_modules symlinks captured
zip -r pkg.zip . -x 'node_modules/*'
# (if symlinks elsewhere) zip -ry captures them as symlinks -> rejected

# after: exclude symlink-heavy trees entirely
zip -r pkg.zip . -x 'node_modules/*' '.pnpm-store/*' '*/.bin/*'
Defensive patterns

Strategy: validation

Validate before calling

func assertOnlyRegularAndDir(path string) error {
    r, err := zip.OpenReader(path)
    if err != nil { return err }
    defer r.Close()
    for _, f := range r.File {
        m := f.Mode()
        if m&os.ModeSymlink != 0 || (!m.IsRegular() && !m.IsDir()) {
            return fmt.Errorf("entry %q has unsupported mode %v", f.Name, m)
        }
    }
    return nil
}

Try / catch

if err != nil { return fmt.Errorf("package contains unsupported file type: %w", err) }

Prevention

When it happens

Trigger: ExtractLocalPackage encounters an entry whose zip FileHeader mode has the symlink bit set or denotes a special file type. Common when archives capture symlink-heavy trees like node_modules/.bin or .cache.

Common situations: The author zipped a directory containing symlinks (node_modules, pnpm cache, framework symlinks); a malicious archive includes device files or symlinks pointing outside the root.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/b494aee1017ae500. Report an issue: GitHub.