siyuan-note/siyuan · error

unsupported rich clipboard image extension [%s]

Error message

unsupported rich clipboard image extension [%s]

What it means

Returned by PrepareRichClipboardAssets when the file extension of an asset path is not in the richClipboardImageExts allow-list. The extension is extracted via filepath.Ext after lowercasing and after stripping any query string via AssetPathWithoutQuery. Only image types registered in that map are accepted for rich clipboard copying.

Source

Thrown at kernel/model/clipboard.go:88

	batch := util.RandString(24)
	groups := map[string]struct{}{}
	copied := map[string]string{}
	ret = &RichClipboardPrepared{Batch: batch}
	defer func() {
		if err != nil {
			cleanupRichClipboardGroups(batch, groups)
		}
	}()

	for _, asset := range assets {
		if asset.Index < 0 {
			return nil, fmt.Errorf("invalid rich clipboard asset index [%d]", asset.Index)
		}

		ext := strings.ToLower(filepath.Ext(AssetPathWithoutQuery(asset.Path)))
		if _, ok := richClipboardImageExts[ext]; !ok {
			return nil, fmt.Errorf("unsupported rich clipboard image extension [%s]", ext)
		}

		absPath, resolveErr := GetAssetAbsPathInBox(asset.Path, asset.Box)
		if resolveErr != nil {
			return nil, resolveErr
		}

		destPath, ok := copied[absPath]
		if !ok {
			group := ExtractBoxIDFromAssetsPath(absPath)
			if group == "" {
				group = richClipboardGlobalGroup
			}
			groups[group] = struct{}{}

			destDir := filepath.Join(util.TempDir, "clipboard", group, batch)
			if mkdirErr := os.MkdirAll(destDir, 0700); mkdirErr != nil {
				return nil, mkdirErr

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Filter the assets array on the frontend to only include image extensions the server supports (PNG, JPG, GIF, etc.) before calling.
  2. If you need a format that is rejected, add it to the richClipboardImageExts map (kernel change) or convert the asset to a supported format.
  3. Confirm AssetPathWithoutQuery is applied client-side too so query strings do not corrupt extension detection.

Example fix

// before: send everything
assets = append(assets, RichClipboardAsset{Path: anyPath})

// after: filter to supported image extensions
var allowed = map[string]bool{".png": true, ".jpg": true, ".jpeg": true, ".gif": true, ".webp": true}
for _, p := range paths {
    ext := strings.ToLower(filepath.Ext(p))
    if allowed[ext] {
        assets = append(assets, RichClipboardAsset{Path: p})
    }
}
Defensive patterns

Strategy: validation

Validate before calling

var richClipboardImageExts = map[string]bool{".png":true,".jpg":true,".jpeg":true,".gif":true,".webp":true,".bmp":true,".svg":true}
func supportedImageExt(path string) bool {
    ext := strings.ToLower(filepath.Ext(stripQuery(path)))
    return richClipboardImageExts[ext]
}

Type guard

func isSupportedImage(path string) bool { return supportedImageExt(path) }

Try / catch

if err != nil && strings.Contains(err.Error(), "unsupported rich clipboard image extension") { filterToImagesAndRetry() }

Prevention

When it happens

Trigger: An asset path points to a non-image file (e.g. .pdf, .docx) or an image format not in the allow-list (e.g. a less common format), or the path has no extension at all. The check runs per asset after the index validation.

Common situations: Paste handler collected a file reference that is not an image; user copied content containing an embedded SVG or HEIC that is not whitelisted; asset URL carries a query string that, even after stripping, leaves an unsupported extension.

Related errors


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