siyuan-note/siyuan · error
staged document tree contains a symbolic link
Error message
staged document tree contains a symbolic link
What it means
While copying the staged temporary document tree into the target notebook, the copier walks each entry and explicitly rejects symbolic links. Symlinks in the staged tree could escape the destination root or break on export, so the copy aborts with this error.
Source
Thrown at kernel/model/import_obsidian.go:2415
func copyObsidianTreeFiles(sourceRoot, destinationRoot string) error {
return filepath.WalkDir(sourceRoot, func(current string, entry fs.DirEntry, walkErr error) error {
if walkErr != nil {
return walkErr
}
rel, err := filepath.Rel(sourceRoot, current)
if err != nil {
return err
}
if rel == "." {
return nil
}
destination := filepath.Join(destinationRoot, rel)
if entry.IsDir() {
return os.MkdirAll(destination, 0755)
}
if entry.Type()&os.ModeSymlink != 0 {
return errors.New("staged document tree contains a symbolic link")
}
return filelock.Copy(current, destination)
})
}
func availableObsidianNotebookName(requested string) string {
base := sanitizeObsidianTitle(requested)
existing := map[string]bool{}
boxes, _ := ListNotebooks()
for _, box := range boxes {
existing[strings.ToLower(box.Name)] = true
}
if !existing[strings.ToLower(base)] {
return base
}
for index := 2; ; index++ {
candidate := fmt.Sprintf("%s (%d)", base, index)
if !existing[strings.ToLower(candidate)] {View on GitHub (pinned to 8641553a1f)
Solutions
- Inspect the staging directory for symlinks (find -type l) and remove them, then retry the import
- Re-run the import with a clean temporary directory so stale links are not copied
- Ensure no tool between staging and copy replaces files with symlinks (sync/backup/link farms)
- If symlinks are intentional, replace them with real file copies before import
Example fix
// before: symlink present in staging dir gets rejected
// staged/assets/logo.png -> /etc/passwd (symlink)
copyTree(stagedDir, destinationRoot) // errors
// after: dereference real copies before staging
filepath.Walk(vaultAssets, func(p string, info os.FileInfo, err error) error {
if info.Mode()&os.ModeSymlink != 0 {
target, _ := os.Readlink(p)
return os.Remove(p) // or copy target contents over the link
}
return nil
}) Defensive patterns
Strategy: validation
Validate before calling
err := filepath.WalkDir(stagedDir, func(p string, d fs.DirEntry, err error) error {
if err == nil && d.Type()&os.ModeSymlink != 0 {
return fmt.Errorf("symlink in staging: %s", p)
}
return err
}) Type guard
func isSymlink(d fs.DirEntry) bool { return d.Type()&os.ModeSymlink != 0 } Try / catch
if err := copyStagedTree(src, dst); err != nil && strings.Contains(err.Error(), "symbolic link") {
// clean staging dir, remove links, retry
} Prevention
- Always start from a fresh staging directory per import run
- Never place symlinks into the staging tree; copy real files instead
- Scan for -type l entries before copying
When it happens
Trigger: filepath.WalkDir over the staged docs temp directory encounters a file entry whose Type() has os.ModeSymlink set — i.e. someone or something placed a symlink into the staging directory before/during the copy to destinationRoot.
Common situations: A malicious or buggy step (or third-party tool) created symlinks in the temp staging directory; a user pre-seeded the temp directory with symlinked assets; importing on a filesystem that materializes links (some cloud mounts); a previous interrupted run left links behind.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- symlink resolves into encrypted notebook [%s]
- archive entry resolves outside destination [%s]
- marketplace package contains an unsupported file
- symlink escapes workspace: %s
- asset path resolves outside notebook assets directory
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/55f376f4c9565a09.
Report an issue: GitHub.