siyuan-note/siyuan · error

exporting symbolic links is not supported

Error message

exporting symbolic links is not supported

What it means

copyExportResource refuses to export a top-level source that is itself a symbolic link, detected via os.Lstat mode. SiYuan rejects symlinks in resource exports to avoid leaking files outside the workspace and to keep exports self-contained.

Solutions

  1. Replace the symlink with a real copy of the target file/folder before exporting
  2. Exclude symlinked resources from the export selection
  3. Re-sync or re-import the asset as a regular file into the workspace

Example fix

// before (fails)
ln -s /external/library.png assets/library.png
// after
cp -L assets/library.png /tmp/library.png && mv /tmp/library.png assets/library.png
Defensive patterns

Strategy: validation

Validate before calling

info, err := os.Lstat(path)
if err == nil && info.Mode()&os.ModeSymlink != 0 { return errors.New("resource is a symlink; copy it first") }

Type guard

func isSymlink(path string) bool {
    info, err := os.Lstat(path)
    return err == nil && info.Mode()&os.ModeSymlink != 0
}

Try / catch

if _, err := model.ExportResources(paths); err != nil {
    // replace symlinks with real copies, then retry
}

Prevention

When it happens

Trigger: ExportResources called on a resource path (file or folder) that is a symlink, e.g. assets linked to an external directory.

Common situations: Users replacing asset files with links to save space (common with cloud-drive folders, macOS alias-style setups, or ln -s into shared libraries).

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/97253987903676ba. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/export.go:939

	}

	exportFilePath = path.Join("temp", "export")
	if encryptedBoxID != "" {
		exportFilePath = path.Join("temp", "export", registerManagedEncryptedExport(encryptedBoxID, "resources", zipFilePath))
	} else {
		exportFilePath = path.Join(exportFilePath, filepath.Base(zipFilePath))
	}
	return
}

// copyExportResource 复制导出资源,目录逐文件处理以避免将加密资源作为普通文件读取。
func copyExportResource(source, destination string) error {
	info, err := os.Lstat(source)
	if err != nil {
		return err
	}
	if info.Mode()&os.ModeSymlink != 0 {
		return errors.New("exporting symbolic links is not supported")
	}
	if !info.IsDir() {
		return copyExportFile(source, destination)
	}

	return filepath.WalkDir(source, func(current string, entry fs.DirEntry, walkErr error) error {
		if walkErr != nil {
			return walkErr
		}
		if entry.Type()&os.ModeSymlink != 0 {
			return errors.New("exporting symbolic links is not supported")
		}
		relativePath, relErr := filepath.Rel(source, current)
		if relErr != nil {
			return relErr
		}
		target := filepath.Join(destination, relativePath)
		if entry.IsDir() {

View on GitHub (pinned to 9f775e8a12)